public class HelloWorld
{
    public static void main( String[] args )
    {
        System.out.println( "Hello World!" );
    }
}

 

이 코드를 실행하면 뭐가 나올까?

 

Console 창에 Hello World! 라고 나온다.

 

혹시 Hello World!를 보고 나도 모르게 웃음이 나온 기억이 있다면, 혹은 지금 그랬다면

 

당신은 좋은 개발자가 될 것이다.

WebUI 테스트 자동화 구성 - Selenium, gitlab, jenkins, TestNG, TestLink, 


TestCase - Resource 생성/수정/삭제/조회


어려운점 - 1. 생성/수정/삭제/조회에 대한 기댓값, 결과값 판단의 기준

    2. testng로 만든 테스트 코드가 젠킨스 빌드시에  실행이 안되던 문제는 testng.xml 파일을 만들어주지 않아서 그랬다. 

    3. 테스트링크와 각각의 테스트 케이스 연동할 때 처음이라 판단이 어려웠음 

-  TestLink 케이스에 Test Program(커스텀 필드로 설정된 부분)에  testng-result.xml에서 확인하면 보이는       ClassName#test-methodName으로 명명해주면 된다.  (ex com.test.Test클래스의 T01 메소드라면, TestLink   TestProgram에 com.test.Test#T01로 명명해주면 인식한다.)

    4. ChromeDriver에서 계속 에러가 나던 문제. 윈도우에서 테스트 할때는 문제가 없었는데 이걸 리눅스 서버로 옮겨서 하려니          문제가 발생 

      4 - 1. Chrome을 창이없이 사용할 수 있도록 해줘야 한다. 이는 ChromeOption에서 "--headless"를 지정해주면 이것이 창을                 띄우진 않지만 실제 창을 띄우는 것과 같은 역할을 해준다.  (크롬 버전이 60이상이어야함)

4 - 2. 간단하게 로그인을 하는 예제 코드를 사용했는데 element에서 바로 click()을 사용하는 경우 에러가 났다. 왜냐하면 화           면이 직접 뜨는 것이 아니기 때문.. 따라서 JavaScriptExecutor 를 사용해서 자바스크립트로 클릭을 하면 성공적으로             수행할 수 있다.

    5. img 클릭 시 tag값에서 img src 값을 직접 지정해야 클릭이 가능하다.

용어정리


XA : eXtended Architecture의 줄임말로 말 그대로 확장된 아키텍쳐이다.

Non-XA : XA의 용어를 활용해보면 확장되지 않은 아키텍쳐이다.

트랜잭션 : 거래의 최소 단위이다. 보통  All or Nothing 이라고 얘기한다. 

  즉 거래라는 것은 서로간의 교환인데 한쪽만 성공한다면 데이터가 망가지기 때문에 정합성을 유지하기 위한 단위라고 볼 수 있다.


활용


일반적으로 DB 트랜잭션의 정합성 유지를 위해 XA 또는 NON-XA를 사용한다.


NON-XA : 기본적인 로컬 트랜잭션. 

   사용하는 DB가 하나일 때 Non-XA로 단순하게 Commit과 Rollback을 처리할 수 있다.

   조금 자세히 설명하자면, DB에 세션 하나에 하나의 트랜잭션을 모두 담아 놓고, 둘다 성공하면 커밋하고 하나라도 실패하면 세션을 롤백 시킨다.


XA : 확장된 아키텍쳐라는 말처럼 DB가 여러개 이거나, 트랜잭션의 주체가 여러개일 때 사용할 수 있다. (그 외에도 다양한 상황에서 사용가능하다.)

 보통 2pc (two-phase-commit)으로 진행되며, DB가 2개일 때를 예로 간단하게 설명하자면, 


 - 양쪽 DB에 준비가 되었는지 먼저 물어본다. -> 양쪽 모두 준비가 되었다고 한다. -> 준비가 완료되었다. -> 커밋을 진행한다.


 이렇게 진행이 된다. 여기서 한쪽이라도 문제가 있다면, 전부 롤백이 된다.



아직 XA를 제대로 활용해본적은 없지만, DB에 정합성에 중요한 역할을 하는 XA, Non-XA에 대해서 간단히 정리해 보았다.

현재 게시판 클래스를 정리하는 작업을 진행 중이다. 


지저분하지만, 정리하기전에 코드를 한번 남겨두고 싶어서 캡쳐해 보았다. 



 


여기있는 자바 코드들이 게시판을 구동하는 주요 엔진이라고 생각하면 된다.



현재 : 모든 처리가 BoardController 하나에 의해서 처리된다. 


정리 후 : 각 모듈별로 Controller를 하나씩 만들 계획



현재 : Command 부분이 전부 하나의 패키지 안에 있다.


정리 후 : 각 모듈별로 패키지를 분리할 계획 



DAO나, DTO는 안건드려도 될 것 같다.



Util에 있는 LoginChecker는 세션에서 로그인 정보를 관리해준다고 해야하나.. 아무튼 전체적으로 관리?? 해준다. 조금 더 공부해야겠다. 



PagingList는 페이징 처리를 해주는데 도움을 주는 친구이다.




오른쪽의 jsp파일은 웹페이지화면을 뿌려주는 친구들이다. 일반적으로 View라고 한다. 



중간중간 DB체크를 위해 만들었던 친구도 있고,



무엇보다 페이지 이름들이 정리가 덜 되어 있다. 



이 부분도 정리를 할 것이다. 



정리하면서 코드도 반복적으로 쓰이는 것도 정리하고 리팩토링??? 초보자지만 그래도 한번 도전해보련다.



다 정리되면 또 올려보겠다.

지난번 게시판 DB구조 만든 이후로 현재 스프링으로 게시판을 거의 다 만들었다. 


현재까지 완료된 작업을 살펴보자면,,


- 글 목록, 글 내용, 글 작성, 글 변경, 글 삭제, 댓글 작성, 댓글 변경, 댓글 삭제,  회원가입, 로그인, 회원 정보 변경, 회원 탈퇴 정도의 구성을 갖고 있다. 


앞으로 여기에 파일 업/다운로드, 코드 주석 달기, 리팩토링 이후 AWS나 도메인 등록을 통해서 실제 서비스를 한번 구동해볼 예정이다.


아래는 현재 게시판의 간략한 구조도 이다. 이렇게 써놓으니까 별 기능이 없는 것 같다..... 뭐 더 추가하면 될 것 같다.



(기준: Jenkins-2.132, GitLab-11.0.3)


사전 조건: gitlab과 jenkins가 연동이 되어있어야함.


1. Jenkins에서 GitLab 관련 플러그인을 설치한다. 


2. GitLab에서 User 설정 -> Access Tokens 으로 들어가 Name과 Expires at을 설정한다. 


3. Jenkins에서 Credentials를 만든다. GitLab API Token을 만든다. Token에는 아까 GitLab에서 만든 Token을 넣어준다.


4. Jenkins관리 -> 시스템 설정으로 들어간다.


5. GitLab 탭에서 Connection name(아무거나), host URL (https://gitlab.com <- 이걸로!) credentials는 아까 만든걸 넣어준다.


6. Jenkins 프로젝트 만든다 (freestyle)


7. 구성에 들어간다. -> gitlab connection에 방금 만든 connection name이 들어가 있다. 


8. 소스 코드 관리에 git을 클릭한다. url에 gitlab에서 만든 프로젝트 url을 넣어준다. Credentials는 새로 하나 만든다. (Username with Password)로 gitlab 정보 넣어준다.


9. 빌드 유발에 Build when a change is pushed to Gitlab 클릭한다. 고급에 secret token을 생성한다. 


10. 다시 Gitlab으로 돌아간다. setting -> integrations 에서 webhooks를 설정한다. URL은 URL은 http://호스트/project/작업이름 SecretToken은 아까 Jenkins에서 만든 Secret token을 넣어준다. (이때 테스트 시 404에러가 나는 경우 jenkins 아이디 비번을 URL에 추가한다. ex )  http://ID:PWD@호스트/project/작업이름)


10-1. 만약 gitLab과 jenkins가 같은 로컬서버에 설치된 경우 gitLab에서 root계정으로 접속하여 admin area에서 setting-> outboundRequest에서 Allow requests to the local network from hooks and services설정을 체크해주어야 정상적으로 Webhook이 동작한다.


11. 다시 jenkins로 돌아가 설정을 마무리한다. Build부분에는 자신이 사용할 sh를 설정하고 빌드 후 조치에 Build_name을 입력하고 저장한다.


12. gitLab에서 push event를 해본다. 


13. 자동으로 빌드되면 성공

젠킨스를 설치한 후 실행하려는데 계속 아래와 같은 에러가 나왔다. (우분투)
Job for jenkins.service failed because the control process exited with error code.
See "systemctl status jenkins.service" and "journalctl -xe" for details.
invoke-rc.d: initscript jenkins, action "start" failed.
● jenkins.service - LSB: Start Jenkins at boot time
   Loaded: loaded (/etc/init.d/jenkins; generated)
   Active: failed (Result: exit-code) since Thu 2018-04-19 10:03:05 UTC; 9ms ago
     Docs: man:systemd-sysv-generator(8)
  Process: 27282 ExecStart=/etc/init.d/jenkins start (code=exited, status=7)

이 에러에는 다양한 이유가 있지만 나같은 경우는 port번호 설정을 제대로 안해서 난 에러 였다.

StackOverFlow를 보니 대부분 자바 버전때문인 것 같다. java 버전이 8이 아닌 경우 호환성 문제가 있는 듯

(이 경우는 /etc/init.d/jenkins PATH에 자바 설치 경로를 추가)

나 같은 경우는 포트 번호만 변경하면 되는데 두군데를 바꿔줘야 하는 것 같다. 하나를 못찾아서 해멨다.

/etc/init.d/jenkins에 HTTP_PORT와 /etc/default/jenkins에 HTTP_PORT를 모두 변경해야 한다.



좋은 글이 있어서 한번 가져와 봤다. 



경력 개발자 역량 평가

  • 이 문서는 개발자 역량 측정 매트릭스의 일부를 참조하여, 우리 회사 실정에 맞도록 변형하였습니다
  • 원문은 programmer-competency-matrix입니다
  • 경력자의 경우 가장 중시하는 건 역시 업무 평가입니다. 그러나 우리는 개발자의 업무 평가가 정량화될 수 없음을 알고 있습니다. 어느 정도의 객관적인 평가를 하기 위해서는 타인의 시선이 필요함을 알고 있지만, 문서상의 한계로 문제를 해결하는 방법에 가장 큰 비중을 두기로 결정했습니다.
  • 그러나 때에 따라 이것이 불가능한 조직에 속해 있었을 수도 있기 때문에 맨 마지막 항목에 가장 집중했었던 분야 또는 업무 개선에 대한 경험을 어필해주세요

전산학 이해

1. 자료구조

  • 배열(array)과 링크드 리스트의 차이를 모른다
  • 실제 프로그래밍 환경에서 배열과 Linked List, dictionary의 차이를 설명할 수 있다
  • 배열과 링크드 리스트의 속도/메모리 타협 관계를 설명할 수 있다. 해시의 충돌 처리를 할 수 있고 우선순위 큐를 만들 수 있다
  • B트리, 이진트리, 피보나치 힙, AVL 트리, RED/BLACK 트리, Splay 트리, Skip 리스트 등 고급 자료구조에 대해 이해하고 있다

2. 알고리즘

  • 배열에 들어있는 숫자들의 평균을 낼 줄 모른다
  • Sorting, Searching, Traversing 알고리즘을 이해한다
  • 트리, 그래프, 단순한 그리디 알고리즘, Divide and Conquer를 이해한다
  • 동적 프로그래밍 솔루션을 이해한다. 그래프 알고리즘, 수치연산 알고리즘을 이해하고 NP 문제를 식별할 수 있다

3. 시스템 프로그래밍

  • 컴파일러, 링커, 인터프리터를 구분하지 못한다
  • 컴파일러, 링커, 인터프리터를 이해한다. 하드웨어 레벨의 어셈블러 언어 동작을 이해한다. 가상 메모리와 페이징에 대한 이해가 있다
  • 커널모드/유저모드의 차이를 알고, 멀티스레딩, 동기화를 이해하고 그것들이 어떻게 구현되었는지 안다. 어셈블리 코드를 읽을 수 있다. 네트워크가 어떻게 동작하는 지 안다. 프로토콜을 알고, 소켓 수준의 프로그램을 읽을 수 있다
  • 전체 프로그래밍 스택을 이해한다. 하드웨어(CPU, 메모리, 캐시, 인터럽트, 마이크로코드), 바이너리 코드, 어셈블리, 정적/동적 링킹, 컴파일, 인터프리테이션, JIT 컴파일, 가비지 컬렉션, 힙, 스택, 메모리 어드레싱 등을 구분해서 이해한다

소프트웨어 엔지니어링

1. 소스코드 버전 관리

  • 날짜 단위의 폴더를 백업
  • CVS, SVN, VSS 등의 사용을 시작했다
  • CVS/SVN에 대한 능숙한 사용, 브랜치/머지를 할 수 있고, 패치를 만들 수 있고, 저장소 속성에 맞게 만들 수 있다
  • 분산 VCS 시스템을 이해한다. Bzr/Mercurial/Darcs/Git 등을 써봤다

2. 빌드 자동화

  • IDE에서만 빌드할 수 있다
  • 커맨드 라인으로 빌드를 만들 수 있다
  • 빌드 스크립트를 직접 작성한다
  • 문서, 설치 인스톨러, 릴리스 노트를 포함한 빌드 스크립트를 만든다

3. 테스트 자동화

  • 테스트는 테스터의 일이라고 생각한다
  • 유닛 테스트를 짜고 새로 짜는 코드에 유닛 테스트를 작성하고 있다
  • TDD 방식으로 코드를 작성한다
  • 기능적, 로드/성능적, GUI 측면의 테스트 자동화를 이해하고 실현한다

프로그래밍

1. 프로그램 분해

  • 라인 단위의 복사/붙여넣기로 코드를 작성한다
  • 문제를 여러 함수로 나눠서 코딩한다
  • 재사용 가능한 객체로 코드를 작성한다
  • 적절한 알고리즘으로 제네릭/OOP 방법을 써서 변경이 있을 법한 부분은 적절히 캡슐화하면서 코딩한다

2. 시스템 분해

  • 파일 1개 클래스 1개 이상의 범위를 생각하지 못한다
  • 같은 플랫폼 같은 기술 범위 내에서는 문제를 쪼개서 해결책을 설계해 낼 수 있다
  • 다양한 기술과 시스템에 걸쳐 있는 문제에 대한 솔루션을 만들어 낸다
  • 복잡한 제품들을 가시화하여 설계하고 외부 시스템과 연동을 이끌어 낸다. 모니터링, 리포팅, 장애복구 등의 운영 작업도 설계할 수 있다

3. 의사소통

  • 아이디어와 생각을 잘 표현하지 못하고, 맞춤법과 문법이 엉망이다
  • 동료에게 무슨 의도로 말하는지 이야기할 수 있다. 맞춤법과 문법은 좋다
  • 효과적으로 의사 소통할 수 있다
  • 모호한 상황에서 생각/설계/아이디어/스펙을 이해하고 소통할 수 있으며, 상황에 맞게 소통할 수 있다

4. 파일 내의 코드 구성

  • 코드 파일 내에서 구조화가 안 되어 있다
  • 메소드들이 논리적으로든 접근성으로든 어떻게든 구조화되어 있다
  • 코드가 영역별로 그루핑되어 있고, 주석도 잘 되어 있고, 서로 다른 파일 간의 참조도 잘 설명되어 있다
  • 파일은 라이선스 헤더도 있고, 요약 설명도 있고, 주석도 잘 되어 있고, 공백 사용은 일관성이 있고, 파일 자체가 보기 좋게 정렬되어 있다

5. 파일 간의 코드 구성

  • 파일 간 구성에 대한 어떤 구조도 없다
  • 파일들이 폴더로 나뉘어 있다
  • 파일별로 고유한 목적이 있다. 예를 들어 클래스 하나 정의, 기능 하나 구현 등
  • 코드 구성이 설계와 잘 매치되어 코드 파일명만 보더라도 설계에 대한 이해를 할 수 있도록 만든다

6. 코드 가독성

  • 코드 가독성에 대한 고려가 없다
  • 파일, 변수, 클래스, 메소드 등에 대한 이름을 잘 부여한다
  • 통상적이지 않은 코드나 버그 수정이나 전제 조건 등에 대해서 주석을 잘 달아둔다
  • 전제 조건은 asssert로 검증한다. nesting 단계가 깊지 않고, 자연스럽게 코드가 흐른다

7. 방어적 코딩

  • 방어적 코딩이 뭔지 모른다
  • 인수를 다 체크하고, 크리티컬한 전제 조건에 대해서 assert를 사용한다
  • 리턴 값도 체크하고, 예외를 항상 체크한다
  • 방어적 코딩을 하기 위한 자신만의 라이브러리가 있다. 실패 케이스를 시험하는 유닛 테스트 코드를 작성한다

8. 에러 핸들링

  • 정상적으로 작동될 경우만을 염두에 두고 코드를 작성한다
  • 예외/에러가 생성되는 주변에 기본적인 에러 핸들링 코드가 있다
  • 예외/에러로 가도 프로그램이 안정적인 상태에 있도록 유지한다. 리소스, 커넥션, 메모리 등이 깨끗하게 해제됨을 보장한다
  • 가능한 예외 상황을 미리 감지해 내도록 코딩한다. 코드 전체에 대해 일정한 예외 핸들링 정책을 사용한다. 전체 시스템에 대한 예외 핸들링 가이드라인을 만든다

9. IDE

  • 대부분 텍스트 에디팅에 IDE를 사용한다
  • 인터페이스 이면에 숨어있는 IDE 기능을 메뉴에서 효과적으로 불러내서 쓴다
  • 거의 모든 IDE 사용을 단축키로 한다
  • IDE에 매크로를 정의해서 사용한다

10. API

  • 자주 문서를 봐야 한다
  • 기억 속에 자주 쓰는 API는 들어있다
  • API에 대해서 폭넓고 깊이 있는 이해를 하고 있다
  • 자주 호출하는 것에 대해서 단순화시키기 위해 API 위에 라이브러리를 추가로 개발하고 결과적으로 API의 부족한 점은 직접 채운다

11. 프레임워크

  • 코어 플랫폼 밖의 어떤 프레임워크도 쓰지 않는다
  • 유명 프레임워크를 들어는 봤으나 써보지는 못했다
  • 프레임워크를 여러 개 능숙하게 쓰고 해당 프레임워크를 효과적으로 잘 쓰는 전형적인 방법을 알고 있다
  • 프레임워크를 직접 개발한다

12. 요구사항

  • 주어진 요구사항을 코드 스펙으로 바꾼다
  • 스펙에서 비어있는 케이스에 대한 질문을 할 수 있다
  • 전체 그림을 이해하고 스펙으로 정의할 영역 전체를 도출해 낸다
  • 경험에 기반해서 주어진 요구사항에 대해 더 좋은 대안과 플로우를 제시할 수 있다

13. 스크립팅

  • 스크립팅 툴을 모른다
  • 배치파일, 쉘 스크립팅을 한다
  • Perl/Python/Ruby/VBScript/Powershell 류의 스크립팅을 한다
  • 재사용 가능한 코드를 짜고 공개한다

14. 데이터베이스

  • 제대로 데이터베이스에 대한 개념을 학습하지 못했다
  • 데이터베이스의 기본 개념과 ACID, 정규화, 트랜잭션을 이해한다
  • 실행될 쿼리를 염두해서 스키마를 정규화 정의할 수 있고, 뷰, 스토어드 프로시저, 트리거, 사용자 정의 타입을 다룰 수 있다
  • 기본 DB관리, 성능 최적화, 인덱스 최적화, 고급 SELECT 쿼리를 작성할 수 있고, 커서를 이해하며, 데이터가 내부적으로 어떻게 저장되는지 알며, 인덱스가 어떻게 저장되는 지 알고, 데이터베이스 미러링, 복제를 이해하고, 2-페이스 커밋을 이해한다

지식

1. 툴

  • 자주 쓰는 IDE만 알고 있다
  • 유명하거나 표준적인 다른 도구들을 쓸 줄 안다
  • 에디터, 디버거, IDE에 대해 잘 알고 있고 오픈 소스 대체물도 잘 알고 있다
  • 도구를 직접 만들 수 있다

2. 최신 기술 이해

  • 최신 기술을 모른다
  • 해당 분야의 최신 기술을 들어는 봤다
  • 알파 프리뷰, CTP, 베타를 다운로드해 봤다. 온라인 매뉴얼 등을 읽어 봤다
  • 프리뷰를 시험해보고, 뭔가 만들어 봤다

3. 플랫폼 내부

  • 플랫폼 내부에 대해 전혀 모른다
  • 플랫폼 내부가 어떻게 동작하는지 기본 이해를 가지고 있다
  • 플랫폼 내부가 어떻게 동작하는지 잘 알고 있고, 가시화시켜서 플랫폼이 어떻게 코드를 실행시키는지 설명할 수 있다
  • 플랫폼 내부에 대한 정보를 제공하기 위해서 디컴파일, 디버깅, 디스어셈블을 하거나 툴을 만든다

4. 책

  • 간단한 문법을 다룬 책을 위주로 몇 권 읽어 봤다
  • 코드 컴플리트를 비롯한 스티브 맥코넬의 저서 또는 켄트벡이나 마틴 파울러의 저서를 읽어 봤다 (방법론에 대한 책)
  • 이펙티브 C++, 이펙티브 Java, 그 외 각종 실용적이면서 중급 사용자에게 적절히 도움이 되는 책을 읽어 봤다
  • 아트 오브 컴퓨터 프로그래밍, 자바스크립트 닌자의 비밀, 대용량데이터베이스 솔루션 등의 고급 서적을 읽어 봤다

(어떤 책을 읽어봤는지 추가로 서술해주셔도 좋습니다)

외국어

1. 외국어 수준

  • 한국어가 아닌 자료로는 기술 습득이 불가능하다
  • 영어로 된 기술 교재를 읽고 기술을 파악한다
  • 미국인/영어권 사용자로부터 개발 업무를 지시받고 업무 수행 후에 결과를 보고할 수 있다
  • 영어권 사용자로부터 SW를 발주 받아, 인도/중국인에게 개발 업무를 아웃소싱하고 국내 팀과 연계하여 국제적인 프로젝트를 진행할 수 있다

문서화

1. 문서포맷

  • 문서 작성이 서툴고 뭘 작성해야하는지 자발적으로 판단하지 못한다
  • 프로젝트에 필요한 문서 포맷이 어떤 것인지 알고 있고 적어도 1개 이상의 프로젝트에서 단계별 문서를 작성해 본 적이 있다
  • 여러 SW 프로젝트에서 SW 사용된 다양한 문서 포맷을 작성해 본 적이 있고 확보하고 있다
  • 필요하면 문서 세트를 직접 정의할 수 있고, 가장 좋은 실천법을 알고 있다

2. 오피스 도구 사용

  • 오피스 도구 사용법을 완전히 숙지하지 않은 상태이다
  • 오피스를 이용해서 각종 문서를 작성하지만 문서 포맷이 주어지지 않으면 애를 먹는다
  • 사용법을 완전히 숙지하고 있으며, 단축키도 사용하고 효율적으로 작성할 수 있다
  • 상당한 작업을 단축키로 수행하며 매크로도 직접 정의해서 사용하고 공개판 외의 도구들도 적극적으로 사용하여 효과적인 문서를 생산한다



1. 스프링에 mysql을 연동하기전 제대로 연결이 되는지 확인해보자.


-샘플 코드



로컬은 왠만하면  연결 잘된다.


2. 로컬이 아닌경우....... 나는 우분투에 mysql을 설치하고 윈도우에서 연결하려고 시도했는데 계속해서 에러가 났다....

   

    에러 내역은 'com.mysql.jdbc.exceptions.jdbc4.CommunicationsException: Communications link failure'......


    계속 에러가 나서 정말 미치는 줄 알았다....


    근데 정말 해결법은 간단했다. 우분투에서 /etc/mysql/mysql.conf.d로 들어가서 mysqld.cnf에서 bind-address를 로컬이 아닌 0.0.0.0으로 바꿔줘야

  

    원격으로 접속이 가능하다....... 꼭 확인하고 저처럼 애먹지 마세용...



3. 스프링에 mySql DataSource 추가하는 법 (뭐 그냥 넣어봤음..)


스프링의 pom.xml에 mysql-connector, spring-jdbc를 추가해준다.



servlet-context.xml에 datasource정보를 추가해준다.





게시판만들기의 가장 기본인 DB테이블을 구성해보았다.


MySql WorkBench를 활용하여 만들어 보았다.


Board 테이블은 기본적인 게시판의 구조를 갖고 있다.

그리고 회원관리를 위한 User 테이블과 댓글을 기록하기 위한 Reply 테이블을 만들었다.


일단 기본적인 것만 생각했고 추후에 수정이 있을지도 모르겠다.

+ Recent posts