매개변수를 포함하는 item의 build를 실행할때 아래와 같이 command line에서 실행하면 된다.  *curl에 없는경우 설치

curl -X POST http://{ip:port}/job/{job_name}buildWithParameters?{parameter name}={parameter value} --user {id}:{token}

 

일반 item의 빌드 명령은 아래와 같다

curl -X POST http://{ip:port}/job/{job_name}build --user {id}:{token}

 

*token 발급 방법

DashBoard -> 사람 -> 대상 id 선택 -> 설정 -> API Token

Generate 버턴을 누르면 생성되며, 생성된 key는 잘 보관해두자. 다시 확인할때는 발생일자만 나오고 값은 확인이 안된다.

Jenkins를 활용하여 MFC(Window app) 프로그램의 CI/CD환경을 구성한다.

1. Jenkins에 svn, msbuild 플러그인 설치

2. MSBuild 설정

3. svn 계정설정.

4. Item(Job)생성
5. Tag별로 빌드 구성

 

Jenkins 관리 -> 플러그인 관리
설치 가능 선택 후 "subversion" 검색하여 항목 선택 후 "Install after restart"
MSBuild 검색 후 설치 
Jenkins 관리 -> Global Tool Configuration.
MSBuild가 설치된 경로 설정 후 SAVE
New Item 생성 -> 이름입력, FreeStyle project "OK"
General 상단에 이 빌드는 매개변수가 있습니다. 체크 후 List Subversion Tags 선택
Credentials->add를 눌러서 SVN계정 추가
소스 코드 관리에서 Subversion 설정

Repository URL, svn 주소 CI/CD는 지속 통합 지속 배포가 목적이기에 배포목적인 tag경로를 설정한다.

위에 매개변수로 설정한 rel_version을 사용하는 경로로 지정한다.

Tags 디렉토리 구조가 아래와 같으면

Tags - rel_v2/client/projects/project1/project1.sln

      └ rel_v3

      └ rel_v4

      └ rel_v5...

 

rel_v2/client/projects/project1/project1.sln의 빌드 설정

MSBuild version은 사전에 설정해둔 msbuild 설정을 선택.

MSBuild Build File에는 빌드할 프로젝트 파일 경로

Command Line Arguments: MSBuild 옵션이며 스크린샷과 같이 실행 시 Win32(x86), Release로 Rebuild 실행한다. 

저장 후  Build With Parameters 선택해서 정상적으로 빌드진행이 되는지 테스트가 가능한다.

매개변수 선택 combobox에 tag 버전 리스트를 선택후 빌드하기를 눌러 진행한다.

최초 진행시 workspace에 svn checkout 을 받으면서 시간이 오래걸린다. 

또, 빌드의 Console Output 으로 진행상태 확인이 가능하다.

MFC로 프로그램을 만들고 보면 이런 경우가 종종있다.


Debug로 했을때는 프로그램이 안뻗는다, 릴리즈로 돌렸을때 뻗는 경우.


이때 가장 먼저 하는게 resource.h 에 중복되는 숫자를 제거하는 방법이지만,


이걸로 안돼면 이 디버그 메시지가 있는지 확인해야한다.


... 첫번째 예외가 발생하였습니다 ...

혹은

... First-Chance exception ...


이 메시지는 잠재적인 문제를 알려주는 것인데, 보통에는 문제가 없어서 지나가기 쉽다.


이 메시지를 디버깅 하기위해서는 환경설정의 디버그 -> 예외 에서 모든 예외 상황을 브레이킹하게 설정해서


디버깅할 수 있다. Access violation 체크!


단축키로는 ctrl + alt + E 를 누른다.





이미지에 표시된 항목을 체크하면 첫번째 예외 메시지가 출력되는 상황에서 디버깅이 걸릴 것이다.




윈도우 프레임을 클릭하여 MFC 프레임크기를 변경할 경우 기본적으로 MFC는 내부컨트롤들을 매번 바꿔준다. 리사이징 과정에서도 컨트롤을 다시 그리기때문에 무거운 컨트롤을 사용중이라면 렉이 걸려 화면조정이 힘든 경우가 있다.


WM_ENTERSIZEMOVE
윈도우 프레임 마우스 클릭 순간

WM_EXITSIZEMOVE
윈도우 프레임 마우스 땐 순간


를 사용하여 해결해 보았다.


WM_ENTERSIZEMOVE 메시지가 온 순간 해당 컨트롤의 SetReDraw를 FALSE 로 변경하고

WM_EXITSIZEMOVE 메시지가 온 순간 해당 컨트롤의 SetReDraw 를 TRUE 로 변경하면서

RedrawWindow로 해주었다.

퍼온곳 : http://webtn.tistory.com/33
1. 쉘 스크립트                       

간단한 쉘 스크립트

$ vi hello.sh

(hello.sh)
#!/bin/bash
echo hello world

$ chmod 755 hello.sh

$ ./hello.sh
hello world


- #! 은 쉘에게 이 프로그램을 실행하기 위해서 #! 다음에 오는 아규먼트를 실행프로그램으로
사용한다는 것을 알려주기 위해서 사용된다.

- Unix/Linux system은 Windows와 달리 파일의 확장자명(.exe .com 등)으로 실행파일 유무를
결정하지 않으며, 파일권한 설정의 변경을 통하여 실행파일로 결정한다. 따라서 스크립트
파일을 실행하기 위해서는 파일에 실행권한을 주어야 한다.


2. 주석                                 

 - "#" 에서부터 라인의 마지막까지가 주석으로 처리된다.#!/bin/bash
# 만든이 : comp-ta
# 하는일 : hello world 를 출력한다.
echo "hello world" # 문자열을 출력한다.


3. 변수                                 

- C 언어와 같은 변수 선언을 필요로 하지 않는다.
- 기본적으로 데이터를 string(문자열)로 저장한다.(수치를 대입해도 실제 수치가 아닌
문자열이 저장된다.) 따라서 타입이 없고 아무 값이나 저장할 수 있다.
- 변수의 값을 사용할 때는 변수명앞에 "$"를 붙여서 사용하고, 값을 대입할때는 "$"를 붙이지
않는다.
- 변수에 데이타를 저장하기 위해서는 대입연산자 "=" 를 사용한다. 대입연산자와,
피연산자/연산자 사이에는 공백이 존재하면 안 된다.
varname=value

- 산술연산 : 모든 것이 문자열로 저장되기 때문에 산술연산을 하기 위해서는 변수=$((산술식))
과 같이 사용.
a=1
b=2
c=$(($a+$b))
echo $c
(3 출력)


4. 명령행 인자(argument)         

  - $1~ $n - 넘겨진 인자들
  - $* : 스크립트에 전달된 인자들을 모아놓은 문자열. 하나의 변수에 저장되며 IFS 환경변수의
     첫번째 문자로 구분된다. (IFS : internal field separator)
  - $@ : $*과 같다. 다만 구분자가 IFS변수의 영향을 받지 않는다.
  - $0 : 실행된 쉘 스크립트 이름
  - $# : 스크립트에 넘겨진 인자의 갯수

(test1.sh)
#!/bin/bash
echo "This Script Executable File : $0"
echo "Argument Count : $#"
echo "Argument List \$* : $*"
echo "Argument List \$@ : $@"
echo "Argument 1 : $1"
echo "Argument 2 : $2"
echo "Argument 3 : $3"
echo "Argument 4 : $4"

(실행)
$chmod 755 test1.sh
$./test1.sh a1 a2 a3 a4
This Script Executable File : ./test1.sh
Argument Count : 4
Argument List $* : a1 a2 a3 a4
Argument List $@ : a1 a2 a3 a4
Argument 1 : a1
Argument 2 : a2
Argument 3 : a3
Argument 4 : a4


5. 제어구조                            

1. [ if / else ]
- 조건을 test하여 참이면 then부분을, 거짓이면 else부분을 실행.
- test 조건들은 "[ ]" 사이에 쓰면된다. "[" 과 "]" 사이에는 반드시 공백문자가 들어가야 한다.
- 변수 값, 파일 특징, 명령의 실행 여부 등 다양한 test 조건이 있다.
 Syntax Example
  if [condition]
  then
    statements
  elif [condition]
  then
    statements...
  else
    statements
  fi
  if [ $1 -lt $2 ]
  then
    echo $1 is less than $2
  elif [ $1 -gt $2 ]
  then
    echo $1 is greater than $2
  else
    echo $1 equals to $2
  fi

- 참고 : test 명령 조건
 문자열비교
  [ string ] - string이 빈 문자열이 아니라면 참
  [ string1 = string2 ] - 두 문자열이 같다면 참
  [ string1 != string2 ] - 두 문자열이 다르면 참
  [ -n string ] - 문자열이 null(빈 문자열) 이 아니라면 참
  [ -z string ] - 문자열이 null(빈 문자열) 이라면 참
 산술비교
  [ expr1 -eq expr2 ] - 두 표현식 값이 같다면 참 ('EQual')
  [ expr1 -ne expr2 ] - 두 표현식 값이 같지 않다면 참 ('Not Equal')
  [ expr1 -gt expr2 ] - expr1 > expr2 이면 참 ('Greater Than')
  [ expr1 -ge expr2 ] - expr1 >= expr2 이면 참 ('Greater Equal')
  [ expr1 -lt expr2 ] - expr1 < expr2 이면 참 ('Less Than')
  [ expr1 -le expr2 ] - expr1 <= expr2 이면 참 ('Less Equal')
  [ ! expr ] - expr 이 참이면 거짓, 거짓이면 참
  [ expr1 -a expr2 ] - expr1 AND expr2 의 결과 (둘다 참이면 참, 'And')
  [ expr1 -o expr2 ] - expr1 OR expr2 의 결과 (둘중 하나만 참이면 참, 'Or')
 파일조건
  [ -b FILE ] - FILE 이 블럭 디바이스 이면 참
  [ -c FILE ] - FILE 이 문자 디바이스 이면 참.
  [ -d FILE ] - FILE 이 디렉토리이면 참
  [ -e FILE ] - FILE 이 존재하면 참
  [ -f FILE ] - FILE 이 존재하고 정규파일이면 참
  [ -g FILE ] - FILE 이 set-group-id 파일이면 참
  [ -h FILE ] - FILE 이 심볼릭 링크이면 참
  [ -L FILE ] - FILE 이 심볼릭 링크이면 참
  [ -k FILE ] - FILE 이 Sticky bit 가 셋팅되어 있으면 참
  [ -p FILE ] - True if file is a named pipe.
  [ -r FILE ] - 현재 사용자가 읽을 수 있는 파일이면 참
  [ -s FILE ] - 파일이 비어있지 않으면 참
  [ -S FILE ] - 소켓 디바이스이면 참
  [ -t FD ] - FD 가 열려진 터미널이면 참
  [ -u FILE ] - FILE 이 set-user-id 파일이면 참
  [ -w FILE ] - 현재 사용자가 쓸 수 있는 파일(writable file) 이면 참
  [ -x FILE ] - 현재사용자가 실행할 수 있는 파일(Executable file) 이면 참
  [ -O FILE ] - FILE 의 소유자가 현재 사용자이면 참
  [ -G FILE ] - FILE 의 그룹이 현재 사용자의 그룹과 같으면 참
  [ FILE1 -nt FILE2 ] - : FILE1이 FILE2 보다 새로운 파일이면 ( 최근파일이면 ) 참
  [ FILE1 -ot FILE2 ] - : FILE1이 FILE2 보다 오래된 파일이면 참
  [ FILE1 -ef FILE2 ] - : FILE1 이 FILE2의 하드링크 파일이면 참 


2. for
 - 지정된 범위 안에서 루프를 수행한다. 범위는 어떤 집합도 가능하다.
 - (Bash 2.03버전 이후 버전) C style의 for loop 사용가능
    = 아래 3 문장은 모두 같은 의미이다 =
    for a in 1 2 3 4 5 6 7 8 9 10
    for a in $(seq 1 10)
    for ((a=0;a<10;a++))
 - 범위가 하나의 string이라면 IFS를 이용해 구분가능.
 - [in list]를 생략한다면 기본값은 “$@”

 Syntax Example
for name [in list]
do
   statements that can use
  $name.
done
IFS=:
for 
dir in $PATH
do
   ls –ld $dir
done



3. while
 - 어떤 조건이 참인 동안 코드를 반복
 Syntax Example
while condition
do
    
statements...
done
count=0
while [ $count -lt 10 ]
do
    echo $count
     count=$(($count+1))
done
4. until
 - 어떤 조건이 참일 때까지 (거짓인 동안) 반복
 Syntax Example
until condition
do
statements...
done
count=0
until [ 
$count -gt 10 ]
do
   
echo $count
    count=$(($count+1))
done


5. case
 - C의 switch구문과 비슷한 역할을 함
 - C의 switch 구문은 정수나 문자 같은 단순한 값을 검사하는 반면, Bash의 case는
 - 와일드카드 (?, *) 사용이 허락되는 어떤 패턴을 놓고 문자열을 검사
 Syntax Example
case expression in
  pattern1 )
    statements ;;
  pattern2 )
    statements ;;
  ...
esac
case $1 in
   *.jpg )
      echo jpg: Graphic
   File!! ;;
   *.txt )
      echo txt: Text File!! ;;
   * )
      echo Unknown File!! ;;
esac

6. select
 - Korn Shell, Bash Shell에만 존재
 - 간단한 메뉴를 제공
 - select의 루프 내에서는 자동적으로 루프를 벗어날 수 없기 때문에 반드시 break문등을
   사용해서 루프를 벗어나야 한다.
 - prompt string을 변경하기 위해서는 쉘변수 PS3를 이용.
 Syntax Example
select name [in list];
do
   statements that can use   $name...
...
done
#!/bin/bash
echo "다음 중 scripting language 에 속하는 것은?"
PS3=”숫자를 선택하세요 : “
select var in "Bash script" "C/C++ "Java” "Exit"
do
if [ "$var" = "Bash script" ]
then
   echo "정답입니다."
   break
elif [ "$var" = "Exit" ]
then
   echo "종료합니다."
   exit 1
else
   echo "$var 을 선택하셨습니다. 오답입니다."
   echo "다음 중 scripting language 에 속하는 것은?"
fi
cf.) read : 직접 사용자 입력을 받음.

#!/bin/bash
echo “이름을 입력해주세요 : “
read NAME
echo "Hi $NAME!"



6. Quoting                             

* Single Quotes
    - Single Quotes 안의 모든 특수 문자를 보통 문자로 취급
    - Single Quotes 안에 또 다른 Single Quote를 쓸 수 없음

$ echo 2 * 3 > 5 is a valid inequality. ………(X)
$ echo '2 * 3 > 5 is a valid inequality.’ ………(O)
$ echo Hatter's tea party ………(X)
$ echo Hatter\'s tea party ………(O)
$ echo 'Hatter'\''s tea party' ………(O)

* Double Quotes
 - Weak Single Quotes
 - Single Quotes와 달리 $, `(backquote), ₩(이스케이프)는 처리
 - 문자열 안에서 다음을 참조할 때 사용
    - 변수확장 : $varname
    - 명령 : $(command) 혹은 `command` -single quote가 아니라 backquote임.
    - 산술연산구문 : $((1+1))

$ echo "2 * 3 > 5 is a valid inequality."
$ echo "Hatter\'s tea party"

$ echo "path=$PATH"
$ echo "current directory=$(pwd)"
$ echo "current directory=`pwd`"
$ echo "1 + 1 = $((1+1))"


7. 함수                                 

 - 다른 프로그램 언어에서와 같이 코드를 재사용하거나 모듈화를 위해 함수를 사용
 - 함수는 함수가 불리기 전에 정의되어야 한다. (C처럼 함수를 미리 선언하는 방법은 없음)
 - parameter : 함수는 자신에게 넘어온 인자를 $1, $2와 같이 인자의 위치로 참조한다.
 - return value : 함수는 반드시 정수값만 리턴할 수 있다. 이 리턴값은 $? 변수에 저장된다.

#!/bin/bash

add()
{

  result=$(($1+$2))

  return $result
}
a=3
b=5
add $a $b
sum=$?

echo "$a + $b = $sum"


8. Backup script code             

#!/bin/bash
if [ -z $1 ]||[ -z $2 ]; then
  echo usage: $0 source_dir target_dir
else
  SRC_DIR=$1
  DST_DIR=$2
  OF=output.$(date +%y%m%d%H%M%S).tar.gz
  if [ -d $DST_DIR ]; then
    tar -cvzf $DST_DIR/$OF $SRC_DIR
  else
   mkdir $DST_DIR
   tar -cvzf $DST_DIR/$OF $SRC_DIR
  fi
fi


휴~~~~~~ 누가 쉽다고 했어 너야! 너야! 너야! 생각 보다 쉽지는 않아요.

하지만 이정도 알아두면 나도 shell script 아는데~ 아는건데~ 아는걸꺼야~ 정도는 될꺼 같은데요 .
난 좀더 응용을 하고 싶다 싶으신 분들

9. 참고하기 

http://wiki.kldp.org/HOWTO//html/Adv-Bash-Scr-HOWTO/index.html

여리글 참고 하시면 엄청 고수가 될수 있어요.



1. /etc/exports 수정

2. sudo service nfs-kernel-server restart


참조


[참조 요약]
LocalAlloc(), GlobalAlloc()
: win16의 잔재. HeapAlloc 보다 느리고 적은 기능.
HeapAlloc()
: 10kb 까진 VirtualAlloc() 보다 빠르며 malloc, new의 segmentation 회피 가능.
VirtualAlloc()
: 가상 메모리 할당받아 페이지 단위로 메모리 조작.
 대용량의 공간 필요시 사용



VirtualAlloc
메모리에 대한 예약 상태 및 액세스 권한 지정이 가능. 
malloc이나 realloc의 메모리 단편화를 극복할 수 있으며 대용량의 메모리가 필요한 경우 유용하다.
보통 시스템 페이지 사이즈(보통 4KB) 단위로 할당된다.

[Overview]
VirtualAlloc() : 가상 메모리 할당
VirtualFree() : 가상 메모리 할당 해제
VirtualProtect(): 접근 권한 변경
VirtualQuery() : 지정한 주소의 페이지 정보 얻기
VirtualLock() : 특정 주소에 특정 길이만큼 메모리 페이지를 잠그며 
잠긴 영역의 데이터는 페이징 파일로 보내지 않고 항상 RAM에 남아있게 된다.
VirtualUnlock() : 데이터가 페이징 파일로 이동할 수 있도록 잠금을 해제한다.

[VirtualAlloc()]
예약(MEM_RESERVE), 확정(MEM_COMMIT) 할당방법 제공. (예약은 64KM 블록 단위로 처리됨)
MEM_RESERVE : 물리적 메모리 확정 없이 주소 공간만 예약
MEM_COMMIT : 물리적 메모리 확정 (실제 사용을 위해 필요)
MEM_TOPDOWN : 가급적 높은 번지의 메모리 할당(NT 이상)
MEM_FREE : 사용할 수 있는 자유 영역
예약과 확정을 동시에 요청할 경우 MEM_RESERVE | MEM_COMMIT 사용
할당 번지(1번쨰 인자)는 할당 단위의 배수가 되는 번지로 내림하여 할당.
NULL시 알아서 적당한 번지를 돌려줌
할당 영역(2번째 인자)은 페이지 단위의 배수 (대부분의 페이지 크기는 4K)
[VirtualFree()]
MEM_DECOMMIT: 확정(MEM_COMMIT) 해제
MEM_RELEASE : 예약(MEM_RESERVE) 해제
[메모리 보호 속성(Memory Protection Constants)]
PAGE_EXECUTE : 실행 가능 (read, write 불가)
PAGE_EXECUTE_READ : 실행, 읽기 가능
PAGE_EXECUTE_READWRITE : 실행, 읽기, 쓰기 가능
PAGE_NOACCESS : 읽기, 쓰기, 실행 금지 (모든 액세스 금지)
PAGE_NOCACHE : 캐시 금지. (시스템 SW에서 사용)
PAGE_READONLY : 읽기 가능
PAGE_READWRITE : 읽기, 쓰기 가능
PAGE_GUARD : 보호 페이지 지정(주로 메모리 끝을 표시하는 용도). 
 이 페이지에 읽기, 쓰기 시도시 STATUS_GUARD_PAGE 예외 발생 후 보호 페이지 상태 해제
PAGE_EXECUTE_WRITECOPY : 공유 영역에 쓰기시 사본을 작성함.
PAGE_WRITECOPY
PAGE_WRITECOMBINE :
**. 실행은 해당 메모리의 코드를 CPU가 실행할 수 있다는 뜻으로 아직까진 읽기와 같은 뜻으로 쓰인다.

[샘플]


HeapAlloc
가상 메모리 공간의 예약된 영역 사용
작은 메모리 블록을 할당하여 사용할때 VirtualAlloc보다 유리하다.
ex) 4byte 크기로 10000번 메모리 할당할 경우
VirtualAlloc은 40MB 가상메모리와 660MB 주소공간을 사용
(한번에 할당시 4KB(페이지 사이즈) + 주소공간 64KB)
HeapAlloc은 40KB만 사용하게 된다.
**.Default Heap : 프로세스 생성시 OS가 기본적으로 할당하는 1MB의 힙

[Overview]
GetProcessHeap() : Default Heap의 핸들을 반환한다.
HeapCreate() : Heap 생성
HeapAlloc() : Heap 에 메모리 할당
HeapFree() : Heap 의 메모리 해제
HeapDestroy() : Heap 해제 (HeapFree로 일일이 해제할 필요 없음)
HeapLock() : 현재 스레드가 Heap에 대한 독점적인 소유를 얻음
HeapUnlock() : 다른 스레드가 사용할 수 있도록 락 해제
HeapReAlloc() : Heap 크기 재할당
HeapSize() : Heap 크기를 구함
HeapCompact() : Heap 영역에 대한 조각모음
HeapValidate() : 유효한 블록인지 검사
HeapWalk() : Heap의 모든 블록 열거(잘못된 블록을 탐색하거나 디버깅시 유용)
**.Heap 할당 방식 지정 flag (보통 0으로 어떤 옵션도 사용하지 않음)
HEAP_GENERATE_EXCEPTIONS : 에러 발생시 NULL 리턴 대신 예회 발생
HEAP_NO_SERIALIZE : Heap사용에 대해 스레드 동기화를 하지 않음
(단일 스레드일때 빠른 속도를 위해 사용할 수 있지만 안정성을 위해 사용은 비권장 됨)
HEAP_ZERO_MEMORY : 할당한 HEAP을 0값으로 초기화

ex)
//옵션은 0, 4096 byte만큼 초기화 하며 1MB(1048576)까지 사용 가능
//1048576대신 0이면 무제한을 의미한다.
HANDLE TestHeap = HeapCreate(0, 4096, 1048576); 
int* ptr = (int*)HeapAlloc(TestHeap, 0, sizeof(int)*10); //40 byte 할당
for (int i=0; i<10; i++) ptr[i] = i*2;
HeapFree(TestHeap, 0, ptr); //메모리 해제
HeapDestroy(TestHeap);



GlobalAlloc/LocalAlloc
Win16에서 사용되던 함수로 Win32는 전역힙, 지역힙의 구분이 없기 때문에 동일하게 동작한다.
보통 GlobalAlloc이 사용된다.
GlobalAlloc, GlobalLock, GlobalUnlock, GlobalFree, GlobalReAlloc 함수들이 있다.
[GlobalAlloc() 의 flag 인자]
GMEM_FIXED : 메모리 할당 후 리턴된 포인터를 바로 캐스팅해 사용 가능
GMEM_MOVEABLE: OS가 효율적인 메모리 관리를 위해 메모리 이동을 허가해주며 포인터대신
  메모리 핸들을 리턴.
GMEM_ZEROINIT: 할당된 메모리를 0으로 초기화
GPTR : GMEM_FIXED | GMEM_ZEROINIT
GHND : GMEM_MOVEABLEL | GMEM_ZEROINIT
**.GMEM_MOVEABLE 사용시 GlobalLock을 통해 메모리 이동을 정지시키고 포인터로의 조작이 가능
포인터로 작업이 끝나면 GlobalUnlock으로 다시 OS가 효율적인 관리를 위한 메모리 이동을 허가해준다.

ex) GMEM_MOVEABLE 형식의 사용
hMem = GlobalAlloc(GMEM_MOVEABLE, sizeof(int)*10);
ptr = (int*)GlobalLock(hMem);
for (int i=0; i<10; i++) ptr[i] = i*2;
GlobalUnlock(hMem);
GlobalFree(hMem);

ex) GMEM_FIXED 형식의 사용
ptr = (int*)GlobalAlloc(GMEM_FIXED, sizeof(int)*10);
for (int i=0; i<10; i++) ptr[i] = i*2;
GlobalFree(hMem);

 

퍼옴 :yamoe.tistory.com/208 

ui->centralWidget->setFixedSize


(width, height); layout()->setSizeConstraint(QLayout::SetFixedSize);
mainwindow.cpp 에서 사용한다.


+ Recent posts