현재 Quartz 는 버젼이 2.2.1 까지 릴리즈됐음 (2013년 9월 24일)


JDK 1.6에서 빌드되어서 배포중 이다.


JAVA 1.5 에서 시스템을 운영중이라면 Quartz 1.7.3 이전버젼을 사용해야된다.

http://quartz-scheduler.org/downloads



Posted by 빨강토끼
,

절차형이 아니고 집합적적으로 생각하라.


정적 파티션과 동적파티션

정적파티션이 흔히 말하는 파티셔닝 테이블이다.


Skew 현상
데이터의 대량 삭제시 인덱스구조가 한쪽으로 쏠리는 현상

정기적으로 리빌드해주는것을 추천


블럭체이닝

다이나믹SQL , 스태틱SQL

스태틱SQL은 매번 SQL을 파싱하여 최적화하는 단계를 거침

다이나믹 SQL 은 컬럼등을 변수처리하여 파싱 및 최적화 단계를 건너뜀


Redo log

DB 장애등의 원복시 사용, Rollback 때도 아마 사용


http://www.techonthenet.com/sql/index.php

http://www.techonthenet.com/oracle/index.php

공부해올것


Posted by 빨강토끼
,

DB 솔루션은 개발자가 직접 작업을 컨트롤 하지 못한다.

다만 SQL 이라는 언어를 통하여 DB 솔루션과 대화를 하며 개발자가 원하는 데이터를 요청하고

응답 받을 있다.

 

이해를 쉽게 하기 위하여 도서관을 DB 솔루션이라고 가정하고

도서를 대여하기 원하는 우리를 개발자로 가정을 하고 설명을 하겠다.

그리고 도서관은 우리가 직접 서고로 찾아가서 책을 찾지 못하고

사서에게 자신이 원하는 책을 말하고 사서가 책을 서고에서 찾아서 도서를 대여해 준다.

 

우리는 도서관에 가서 원하는 책을 찾으려고 한.

우리가 원하는 책을 가장 빨리 사서를 통하여 찾는 방법은 바로

도서관의 책에 부여하는 도서 일련번호를 바로 말하는 것이다..

 

도서관은 도서들만의 유일한 코드를 가지고 있고

도서일련번호순서대로 도서를 관리하고 있으므로

도서일련번호만 말하면 사서는 바로 책을 잦아서 우리에게 대여를 해줄 있다.

이것이 쉽게 말하면 오라클의 rowid 이해할 있다.

 

다음으로는 대분류(장르) 소분류(세부장르) 구별된 사항과 책의 이름을 말하는 것이다.

물론 출판사나 저자의 이름도 유용하다.

 

장르, 세부장르, 출판사, 저자 들은 인덱스로 이해할 있다.

 

책의 색깔이나 페이지수 등은 책의 특징이라고 있다.

 

그렇다면  PK 무엇일까?

도서일련번호와는 다르게 도서관에서 분류 이해를 쉽게 하기 위하여

역시 책마다 유일하게 정의할 있도록 도서코드

( 예를 들어 대분류-소분류-출판사-출판사별책순서) 부여해 놓은 것이라고 있다.


# 도서코드

 o 대분류

 o 소분류

 o 도서이름

 o 저자

 o 출판사

 o 표지색상

 o 페이지수

 o 출판일

 o 대출횟수


 

그러면 옵티마이저는 무엇일까?

우리가 사서에게 대여를 원하는 책을 말할때

이렇게 수가 있겠다.

(당연히 책의 일련번호나 도서코드를 모르는 상태에서)

 

"빨간색 책인데요. 패이지수는 250페이지입니다.

도서이름은 인간관계론 입니다자기개발서적이구요.

아참! 저자는 데일 카네기입니다."


SELECT * FROM 도서

WHERE 표지색상='빨간색' 

  AND 페이지수=250 

  AND 도서이름='인간관계론'

  AND 소분류='자기개발'

  AND 저자='데일 카네기'


말을 듣고 만일 사서가 무식하게 도서관의 모든 책을 하나씩 보면서

  1. 표지색상: 빨간색
  2. 페이지수 : 250 페이지
  3. 도서이름 : 인간관계론
  4. 소분류 : 자기개발
  5. 저자 : 데일 카네기

위와 같은 순서로 책을 찾는다면 우리는 엄청난 시간을 사서가 책을 찾아오기까지 기다려야  것이다.

 

하지만 영리한 사서는 아마도 아래와 같은 순서대로 찾을 찾으려고 할것이다.

  1. 소분류 : 자기개발
  2. 저자 : 데일 카네기
  3. 도서이름 : 인간관계론
  4. 표지색상 : 빨간색
  5. 페이지수 : 250 페이지

 

보다 경험이 많은 사서는

  1. 소분류 : 자기개발
  2. 표지색상 : 빨간색 (빨간색 책은 몇권 안되므로)
  3. 도서이름 : 인간관계론 (저자보다 책이름이 크게 보임)
  4. 저자 : 데일 카네기
  5. 페이지수 :250 페이지

 

첫번째 사서는 교육시간에 배운 매뉴얼대로 찾은 것이고 (룰기반 옵티마이저)

두번째 사서는 오랜 경험으로 나름의 방법을 사용하는 찾는것 이다.(비용기반 옵티마이저)


그렇지만 결국에는 아무리 도서관 사서가 똑똑해도 우리가 도서를 대여할 정확하게 내가 찾고자 하는

책을 설명해줘야 된다는 것이다.

 

" 책이 제목이 인간.. 머였는데 두꺼웠어요. 아마 200 페이지는 넘을 거에요.

빨간색이였던건 기억나요. 저자는 외국인 이였어요."

 

SELECT * FROM BOOK

WHERE 도서이름 LIKE '인간%'

  AND 페이지수 > 200

  AND 표지색상 = 'red'


 

이렇게 말하면 사서도 고생 여러분도 고생이다.

Posted by 빨강토끼
,

 

임백준 | 오병곤 | 이춘식 | 이주연 | 박재성 | 신재용 지음

상동도서관에서 대여

2014년 3월 25일 ~ 3월 31일

시니어 프로그래머, 행복한 프로그래밍  _임백준

슈퍼스타 프로그래머가 되자
설계 - 코딩 - 테스트
좀더 많은 시간을 설계에 투자하자
코딩스타일에 좀더 신경쓰자
유닛테스트가 가능하도록 코딩하자
코딩이 끝나면 반드시 테스트를 하자

if - else 문은 가급적 자제하자
스스로에게라도 코드리뷰를 하자

스크럼을 하자
(1)나는 어제 무엇을 했는가
(2)나는 오늘 무엇을 할 것인가.
(3)내일의 진행을 가로막는 기술적인 장애가 있는가

오픈소스라이브러리를 오용하거나 남용하는것은 문제다.
자신이 직접 간단하게 프로그래밍 할 수 있는 기능까지 오픈소스 라이브러리를 이용해서 구현하는 것은 비효울적이다.

제2의 인생, 컨설턴트의 길 _ 오병곤

메모하고 정리하자
발표자료를 준비하자
마음에 드는 고객을 만났으면 그 사람을 한달 안에 정말 감동받아 쓰러지도록 만들어라.

첫째. 나만의 전문분야를 만들어 필살기로 계발하자
(깊은 열정을 가진 일 && 최고가 될 수 있는 일 && 경제적 만족도)
둘째. 사람이 재산이다 성공을 도와줄 수 있는 사람을 모으고 끌어당겨라.
셋째. 협소한 엔지니어 마인드를 탈피하가.
넷째. 도전 정신을 갖고 자신의 관심사를 확산시켜 풍부하게 만들어라.

데이터아키텍트의 마스터로 살련다 _이춘식

1년에 1권씩 책을 집필할다는 생각을 가지고 정보와 자료를 정리하자.
수확체증의 법칙을 항상 기억하라.(눈덩이 굴리기 , 계단식성장)

자질이 우수한 리더
1) 일의 목표는 분명한가?
2) 일에 가치가 부여되고 있고 일의 성과를 내고 있는가?
3) 다양한 성격의 사람과 대화를 잘할 수 있는가? 협상의 기술이 있는가?
4)프로젠테이션 스킬이 우수한가?
5)기획 문서를 쉽고 빠르게 작성할 수 있는가?
6)시간 관리를 하고 있는가?
7)변화와 개선점을 파악할 수 있고 제시할 수 있는가?

자격증 및 스킬학습 성공을 위한 7가지 습관
1. Vision을 명확하게 해야 한다.
2. Fun하게 학습한다.
3. Passion을 가지고 학습한다.
4. Pass 마인드를 갖자.
5. Never Give Up 
6. Open / Share
7. Study 한다. (깊이 있게 연구하는 것)

30년 외길 인생, 은퇴를 앞둔 노병의 메시지 _이주연

문서화는 꼭 필요하다.
주기적으로 관리하자

목표를 세우고 남들이 안가본 도전을 해봐라
(솔찍히 100% 동의하진 않음)

은퇴이후를 생각하자.

표준->컨설팅->솔루션->SI(시스템통합)

자바지기의 프로그래머 그 다음 이야기 _박재성

발표를 할 기회를 자주 만들어라

10년차 어느 변방 갑돌이 프로그래머의 우물 안 극복기 _

최소 1년에 한번씩 이력서를 업데이트 하자
10년후의 모습을 그려보자.(10가지)

Posted by 빨강토끼
,

- Edward Garson


Functional Programming has recently enjoyed renewed interest from the mainstream programming community. Part of the reason is because emergent properties of the functional paradigm are well positioned to address the challenges posed by our industry's shift toward multi-core. However, while that is certainly an important application, it is not the reason this piece admonishes you to know thy functional programming.


Mastery of the functional programming paradigm can greatly improve the quality of the code you write in other contexts. If you deeply understand and apply the functional paradigm, your designs will exhibit a much higher degree of referential transparency.


Referential transparency is a very desirable property: It implies that functions consistently yield the same results given the same input, irrespective of where and when they are invoked. That is, function, evaluation depends less - ideally, not at all - on the side effects of mutable state.


A leading cause of defects in imperative code is attributable to mutable variables. Everyone reading this will have investigated why some value is not as expected in a particular situation. Visibility semantics can help to mitigate these insidious defects, or at least to drastically narrow down their location, but their true culprit may in fact be the providence of designs that employ inordinate mutability.



And we certainly don't get much help from industry in this regard. Introductions to object orientation tacitly promote such design, because they often show examples composed of graphs of relatively long-lived object that happily call mutator methods on each other, which can be dangerous. However, with astute test-driven design, particularly when being such to "Mock Roles, not Objects", unnecessary mutability can be designed away. 


The net result is a design that typically has better responsibility allocation with more numerous, functions that act on arguments passed into them, rather than referencing mutable member variables. There will be fewer defects, and furthermore they will often be simpler to debug, because it is easier to locate where a rogue value is introduced in these designs than to otherwise deduce the particular context that results in an erroneous assignment. This adds up to a much higher degree of referential transparency, and positively, and positively nothing will get these ideas as deeply into your bones as learning a functional programming language, where this model of computation is the norm.


Of course, this approach is not optimal in all situations. For example, in object-oriented systems this style often yields better results with domain model development(i.e., where collaborations serve to break down the complexity of business rules) than with user-interface development.


Master the functional programming paradigm so you are able to judiciously apply the lessons learned to other domains. Your object systems(for one) will resonate with referential transparency goodness and be much closer to their functional counterparts than many would have you believe. In fact, some would even assert that the apex if functional programming and object orientation are merely a reflection of each other, a form of computational yin and yang.


객체지향형 프로그램의 단점중에 하나라고 하면 지속적으로 변하는 내부맴버변수들을 효율적으로 컨트롤하지 못한다는 것이다.(동기화나 다른 여러가지 이슈)


함수들이 주어진 같은 입력에 대해 언제 어디서 호출되던지 상관없이 지속적으로 같은 결과를 얻도록 하는것이 참조 투명성의 바람직한 특징이다.


그러기 위해서 가급적이면 변하기쉬운 멤버변수들을 참조하기보다 그것들을 함수의 인자로 전달고 좀더 작고 많은수의 함수들로 책임을 나누게 하여서 훨신 더 높은 참조투명성을 갖게 하자는 것이 함수형 프로그래밍의 취지중에 하나이다.


물론 본문의 끝자락에 나와있드시 함수형 프로그래밍과 객체지향은 음과 양처럼 서로의 장단점이 있으므로 각자에 대하여 깊은 이해를 전제하여 각 과제마다 적절한 방식을 사용하는것이 주요하다.



Posted by 빨강토끼
,

기술이라고 하기에 많이 부족하지만

해당글에는 앞으로 반말을 사용해야겠습니다.


이유는 


http://blog.outsider.ne.kr/831

http://akngs.tumblr.com/post/29967547275

http://www.richis.org/html/research/treatise/a08.pdf


상기 블로그에도 이야기하드시 문장이 좀더 명료하게 간결해진다는 것 때문입니다.


이해해 주세요. ^^


고맙습니다.

Posted by 빨강토끼
,

camel 로 개발을 하고 있는데


http://camel.apache.org/http.html 의 예제를 보면


Sample with scheduled poll

The sample polls the Google homepage every 10 seconds and write the page to the file message.html:

from("timer://foo?fixedRate=true&delay=0&period=10000")
    .to("http://www.google.com")
    .setHeader(FileComponent.HEADER_FILE_NAME, "message.html").to("file:target/google");

라는 예제가 있는데


FileComponent.HEADER_FILE_NAME 라는 구문을 입력하니


해당 constant 를 찾을수 없다는 에러가 난다.


인터넷을 검색해보니 해당constant 는 org.apache.camel.Exchange 로 옯겼다고 한다.


Camel 2.0 이상을 사용한다면 Exchange.FILE_NAME으로 사용하면 된다.


https://issues.apache.org/jira/browse/CAMEL-1733



그리고 하나더


setHeader(Exchange.FILE_NAME, "report.txt") 로 바꾸고 나서도 


The methid setHeader(String, Expression) in the type ProcessorDefinition<RouteDefinition> is not applicable for the arguments (String, String) 

이라는 에러가 난다.


이것은 


setHeader(Exchange.FILE_NAME, constant("report.txt"))  로 바꿔주면 된다.


http://camel.apache.org/file2.html


고맙습니다. 


Posted by 빨강토끼
,

camel 이라는 이름은 들어봤어도 실제 다운로드 받아서 테스트해본것은 오늘이 처음입니다.


EIP (Enterprise Integration Patterns) 라고해서 Enterprise 라는 단어만봐도 어려울것 같아서

멀미증상이 있는저는 그저 딴나라이야기처럼 보였습니다.


spring 은 언제 해볼련지....


암튼 그렇게 어려운 개념도 아니고(물론 상세한 것까지 들어가면 정말어렵지만)

당장 어려가지 경우에 간단하게 적용해볼만한 재미있는 라이브러리였습니다.


정말 간단히 표현한 이미지가 있습니다.


(출처) http://barunmo.blogspot.kr/2013/10/apache-camel-hello-world.html


만일 FTP에 접속해서 파일을 가져온다음에 이것을 로컬디렉토리에 저장하고 그것을 다른 경로에 주기적으로 복사하고 파싱을 한다음에 DB에 저장후 저장 결과를 이메일로 관리자에게 저장하는 비지니스로직이 있다고 하면

가장 골치아픈부분은 각 인터페이스부분을 직접 설계하고 관리해야한다는 것입니다.


하지만 camel은 정말 사막의 낙타처럼 묵묵하게 데이터들을 여기저기로 싣어나릅니다.


애플리케이션은 camel에게 

"이것은 어디서 어디로 옮겨라. 그리고 만일에 동일한 파일명의 파일이있다면 다른이름으로 저장을하고. 아맞다 이런이런 이름의 파일명을 제외하고 몇분마다 주기적으로 copy를 하고. 요런 이름의 파일은 니가 피싱을 해서 DB에다 넣어놔. 다 되면 나에게 말해줘."


이렇게 camel에게 시키기만 하면됩니다.


애플리케이션은 자기 할일만 하면 됩니다.


그럼 우리도 한번 시작해보시죠.


일단 가장좋은 start 방법은 camel 패키지에 있는 예제로 테스트 해보는것입니다.


블로그나 구글링하는것 보다는 패키지에 있는 예제소스가 정말 도움이 많이 됩니다.


http://camel.apache.org/download.html


각 OS에 맞는 바이러리 파일을 다운받습니다.


압축을 풀고나면 examples 디렉토리안에 보면 예제소스들이 있습니다.


camel-example-ftp 디렉토리로 이동해서 보면 중요파일이 아래와 같이 있습니다.


\examples\camel-example-ftp\src\main\java\org\apache\camel\example\ftp

MyFtpClient.java

MyFtpClientRouteBuilder.java

MyFtpServer.java

MyFtpServerRouteBuilder.java


\camel-example-ftp\src\main\resources

ftp.properties


아참 요즘 오픈소스를 컴파일하거나 테스트 해시려면 mvn(메이븐) 은 필수입니다.

구글링을 통해서 꼭 mvn는 꼭 설치하세요.


MyFtpClient* 는 로컬에 있는 파일을 FTP 서버에 upload 해보는것이고

MyFtpServer* 는 반대로 FTP 서버에 있는 파일을 로컬로 download 해보는 것입니다.


MyFtpServer 를 해봅시다.



만일 eclipse에서 테스트해보고 싶으시면

\examples\camel-example-ftp 에서

mvn eclipse:eclipse 라고 명령을 입력하시면

eclipse용 설정파일들이 생깁니다.

import하시면 됩니다.



일단

MyFtpServerRouteBuilder.java 파일과 ftp.properties 파일을 수정합니다.


MyFtpServerRouteBuilder.java

        from("{{ftp.server}}")

            .to("file:로컬다운로드위치")

            .log("Downloaded file ${file:name} complete.");


ftp.properties

ftp.client=ftp:/여러분FTP주소:21/mypath?username=아이디&password=비번


ftp.server={{ftp.client}}&delay=5s&move=done 


위와 같이 수정후에


\examples\camel-example-ftp 에서

mvn compile exec:java -Pserver

라고 명령을 입력하면 여러분의 FTP에서 파일을 로컬로 다운로드받고 

FTP서버의 파일은 done 이라는 디렉토리로 이동되는것을 확인해 보실수 있습니다.


쉽죠?

sftp도 잘됩니다.


[참조]

http://en.wikipedia.org/wiki/Apache_Camel

http://barunmo.blogspot.kr/2013/10/apache-camel-hello-world.html

http://bcho.tistory.com/715


Posted by 빨강토끼
,

LOG4J의 차세대 버전인(사실 공개된지 오래됨) LOGBACK을 GITHUB를 통해 다운받았습니다.


이유는 logback의 주요기능중에  설정파일의 Dynamic Reloading 지원 이라는 것이 있는데

매번 전체 설정정보를 reload 하는지 아니면 변경여부(사이즈 등)을 확인후 변경된 것 같으면

reload를 하는지 궁금해서였습니다.


git clone https://github.com/qos-ch/logback.git


소스를 다운받았으니 빌드(이클립스)를 해보고 싶었습니다.

http://logback.qos.ch/setup.html#ide


IntelliJ 에서는 손쉽게 프로젝트를 import해서 사용해볼수 있는데

이클립스에서는 조금 어렵더군요...


사이트에 있는 데로 번역을 해봤습니다.

1. 이클립스를 설치합니다.

2. Groovy 플러그인을 설치합니다.

    . 자신의 이클립스버전에 맞는 플러그인을 사용해야합니다.확인

    예를 들어 이클립스 4.2(Juno)라면 update site 는 http://dist.springsource.org/release/GRECLIPSE/e4.2/ 입니다.

    . 이클립스에서 "help->install new Software->Work with" 에서 상기 update site 를 입력합니다.

    . Groovy-Eclipse Feature" 를 선택합니다. "m2e Configurator for Groovy-Eclipse"는 필요하지 않습니다.

3. $LOGBACK_HOSE(LOGBACK의 소스를 GITHUB로 다운받은 디렉토리)로 이동합니다.

4. 만약에 .settings, .classpath, .project 파일이 폴더에 있다면 삭제합니다(하위폴더 포함)

5. 명령창에서 mvn eclipse:eclipse 라고 명령을 입력합니다. 여기서 문제가 있었는데 별도로 정리하겠습니다.

6. 이클립스에서 LOGBACK 프로젝트를 import합니다. Import->General->Existing projects 에서 $LOGBACK_HOSE 를 선택합니다.

7. 이클립스에서 logback-classic/target/generated-sources/groovy-stubs/main 디렉토리를 소스폴더에서 제거합니다.

    (logback-classic->project properties->Java Build Path)

8. 이클립스에서 모든 프로젝트를 Clean 합니다.(Project->Clean)

9. 이클립스에서 logback-classic 프로젝트를 선택하고 Groovy project 로 변환합니다.

   (logback-classic 프로젝트에서 Configure->Convert Groovy to Project)



Posted by 빨강토끼
,

불과 저번주까지 log4j만 쓰다가 slf4j를 통해 logback 을 사용해보기로 결정했습니다.

사실 logback이라는게 있다는걸 오늘 알았습니다. ㅡㅡ;

다들 아는 사실이겠지만 log4j를 만든사람이 새롭게 만든 log관련 라이브러리라서 (이유)

log4j를 사용해본 분들이라면 손쉽게 적응할수 있다고 합니다.



http://logback.qos.ch/



간단하게 본인위주로 정리하겠습니다.

자세한 설명은 맨아래 참조란을 따라가시면 됩니다.



1. 새롭게 제공되는 주요 기능 및 개선

 . log4j에 비해 속도나 메모리 사용면에서 개선

 . 설정파일의 Dynamic Reloading 지원

 . 설정파일의 조건부 처리 기능

 . 로그파일에 대한 자동압축, 자동 삭제 기능 제공

 . 런타임에 설정한 값에 따라 로그를 분리하여 처리할 수 있는 SiftingAppender 제공

 . groovy 언어로 설정파일 작성 기능

 . FileAppender 사용 시 다수의 JVM이 동시에 하나의 파일에 로그를 남길 수 있는 prudent mode를 지원

 . 다양한 조건에 따른 로깅처리 여부를 결정할 수 있는 Filter 제공


이것중에 관심이 가는 것은 설정파일의 Dynamic Reloading 지원 , 로그파일에 대한 자동 압축, 자동 삭제기능 제공 입니다.


2. Dynamic Reloading 지원

예전같으면 INFO 모드로 로그를 쌓고 있다가 여러가지이유로 DEBUG모드로 로그를 쌓고 싶을때 

설정화일을 변경하고 서버를 재기동해야 했습니다.

하지만 LOGBACK에서는 설정을 바꾸면 주기적으로 변경사항이 있는지 체크하여 자동으로 설정파일의

내용을 reloading 하여 변경합니다.


방법은

<configuration scan="true" scanPeriod="30 seconds"> ..... </configuration>


이렇게하시면 됩니다.


그럼 30초단위로 설정화일을 스캔합니다.


3. 로그파일에 대한 자동압축, 자동 삭제기능 제공

저역시 하루에 몇기가씩 로그가 쌓이는(INFO인데도...) 서버를 운영중입니다.

그래서 그전에 했던 방법은 주기적으로 하루전의 로그파일을 압축해서 보관하는 스크립트를 작성하여

crontab으로 스케줄링하여 사용하고 있었습니다.

하지만 logback에서는 스스로 이러한 처리를 할수있는 옵션을 제공합니다.


 <configuration scan="true" scanPeriod="30 seconds">
        .....
 </configuration>
 
 <appender name="file" class="ch.qos.logback.core.rolling.RollingFileAppender"> 
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> 
            
            <fileNamePattern> someFileName.log.zip (or gz) </fileNamePattern>

              <maxHistory>90</maxHistory>     
        </rollingPolicy> 
        ....
    </appender>


fileNamePattern : 마지막 확장자를 zip 이나 gz 로 지정하면 자동으로 압축합니다.

maxHistory : (조금 혼란스러웠지만 이제는 이해가 되는 내용이였습니다). 남겨놓을 로그파일의 갯수입니다.

즉 size 단위로 한다고 하면 90개가 넘어가게 되면 옛날것부터 삭제합니다.

그리고 백업을 SIZE 단위가 아니고 매일단위로 백업을 한다면 위에같은 경우에는 

90일동안 보관하다가 삭제를 하게됩니다.

(정확히 말하자면 90개가 넘어가게되면 옛날것부터 삭제).

그리고 만일 매월단위로 백업을 한다면 90개월후에 삭제되겠지요.

이해가 되시나요? 그럼 만일 중간의 파일을 임의로 삭제하게된다면?

저도 테스트해보고 블로그에 남기도록 하겠습니다.


참조

http://dev.anyframejava.org/docs/anyframe/plugin/optional/logback/1.0.2/reference/html/index.html

logback 사용해야 하는 이유 (Reasons to prefer logback over log4j)

Posted by 빨강토끼
,