Whiteship's Note


[스프링 3.0] 로깅은 SLF4J를 이용한 Log4J로

Spring/3.0 : 2010.01.28 17:08


스프링을 사용하면 기본적으로 JCL(자카르타 커먼스 로깅)을 사용하게 되는데 JCL이 실제 로거를 선택하는 시점이 런타임이라 클래스로더 문제라던가 런타임시 오버헤드가 생길 수 있는데 이것을 개선한 구현체 SLF4J로 갈아타면 그러너 문제 걱정을 덜 수 있겠습니다. 보너스로 문자열 연결로 발생하는 오버 헤드도 개선할 수 있으며 귀찮은 if문 추가하는 코딩에서 벗어날 수 있는 문법? API?를 제공해줍니다. 그래서인지 스프링도 3.0부터는 본격적으로 spring-core가 의존하는 JCL을 SLF4J로 교체하고 사용하는 예제 및 레퍼런스 설명을 보여주고 있습니다..

갈아타는 방법은 다음과 같습니다.

1. 스프링에서 참조하는 JCL 라이브러리를 빼버리기.
2. JCL-over-SLF4J 추가하기.
3. SLF4J API 추가.
4. SLF4J-log4j 추가.
5. log4j 추가.

이전에 스프링소스 블로그와 레퍼런스에 올라온 방법은 이걸 그대로 메이븐 의존성으로 옮겨적고 있지요.

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>${org.springframework.version}</version>
            <exclusions>
                <!-- Exclude Commons Logging in favor of SLF4j -->
                <exclusion>
                    <groupId>commons-logging</groupId>
                    <artifactId>commons-logging</artifactId>
                 </exclusion>
            </exclusions>
        </dependency>

        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>${org.slf4j.version}</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>jcl-over-slf4j</artifactId>
            <version>${org.slf4j.version}</version>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>${org.slf4j.version}</version>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.15</version>
            <exclusions>
                <exclusion>
                    <groupId>javax.mail</groupId>
                    <artifactId>mail</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>javax.jms</groupId>
                    <artifactId>jms</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>com.sun.jdmk</groupId>
                    <artifactId>jmxtools</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>com.sun.jmx</groupId>
                    <artifactId>jmxri</artifactId>
                </exclusion>
            </exclusions>
            <scope>runtime</scope>
        </dependency>

1번은 단순히 JCL 라이브러리에 의존할 생각이 없기 때문에 빼버리는 것이고, 그때 JCL에 의존하는 클래스들이 깨질 텐데 그것을 JCL-over-SLF4J를 이용해서 겉은 JCL 같지만 내부에서는 SLF4J API를 호출하도록 일종의 어댑터 나 다리 역할을 해주는 라이브러리를 추가하고, 인터페이스 격인 3. SLF4J API를 추가한 뒤 실제 사용할 로거로 SL4J를 구현한 4. SLF4J-Log4J 라이브러리를 추가합니다. 마지막으로 최종적으로 사용할 로거 5. Log4J를 추가한 것입니다. 이때 Log4J가 불필요하게 참조하는 jmx, mail, jms 등의 라이브러리를 제외시켜줍니다.

만약에 Log4J가 아니라 다른 로거를 사용할 거라면 4번과 5번을 교체하면 될테고 JCL 뿐 아니라 log4j에 대한 직접 호출도 slf4j로 오게 하려면 2번 대신 log4j-over-slf4j을 추가하면 되겠습니다.

굉장히 장황니다. @_@;; 하지만 뭐.. 좋다는데.. 갈아타긴 해야겠죠.

출처: SLF4J in 10 slides, by Ceki Gülcü

위와 같은 설정은 스프링 3.0 예제와 레퍼런스에서 사용하고 있습니다. 정말 장황합니다. 일단 여기서 1, 2번은 필수다 하지만 레퍼런스와 블로그 글에도 나와있듯이 그 이하 3, 4, 5는 Logback이라는 SLF4J API 구현체로 한방 설정이 가능합니다.

logback은 크게 세 가지 모듈로 나뉘는데 그 중에 SLF4J API 구현체인 classic 모듈이 있고, Log4J를 개선한 core 모듈이 있습니다. logback 모듈을 가져오면 위에서처럼 log4j가 끌고오는 부가적인 라이브러리도 없고 깔끔하게 log4j API를 사용할 수 있습니다.

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>${org.springframework.version}</version>
            <exclusions>
                <!-- Exclude Commons Logging in favor of SLF4j -->
                <exclusion>
                    <groupId>commons-logging</groupId>
                    <artifactId>commons-logging</artifactId>
                 </exclusion>
            </exclusions>
        </dependency>

        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>jcl-over-slf4j</artifactId>
            <version>${org.slf4j.version}</version>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>com.springsource.ch.qos.logback.classic</artifactId>
            <version>0.9.9</version>
        </dependency>

따라서 굳이 Log4J가 아닌 다른 로거로 갈아탈 계획이 없다면 이렇게만 설정해도 되겠습니다. 로깅 설정만 거의 1/3로 줄어듭니다.

사용법은

1. SLF4J API를 이용해서 로거를 만들고..

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Logger log = LoggerFactory.getLogger(AccountJsonBindingTests.class);

2. 다음과 같이 로깅하면 됩니다.

log.info("name is {}", name);

+를 사용해서 문자열 연산을 할 필요도 없고, if문을 줘서 문자열 연산을 막을 필요도 없습니다.

3. log4j 설정은 프로퍼티 파일이나 XML로 할 수 있는데 스프링 3.0 예제는 보통 XML을 사용하더군요. src와 test 소스 폴더 하위의 클래스패스 루트에 각각 다음과 같은 log4j.xml 파일을 둡니다.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE log4j:configuration PUBLIC "-//LOGGER" "log4j.dtd">

<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">

    <!-- Appenders -->
    <appender name="console" class="org.apache.log4j.ConsoleAppender">
        <param name="Target" value="System.out" />
        <layout class="org.apache.log4j.PatternLayout">
            <param name="ConversionPattern" value="%-5p: %c - %m%n" />
        </layout>
    </appender>

    <!-- 3rdparty Loggers -->
    <logger name="org.springframework.core">
        <level value="info" />
    </logger>

    <logger name="org.springframework.beans">
        <level value="info" />
    </logger>
   
    <logger name="org.springframework.context">
        <level value="info" />
    </logger>

    <logger name="org.springframework.web">
        <level value="info" />
    </logger>

    <!-- Root Logger -->
    <root>
        <priority value="warn" />
        <appender-ref ref="console" />
    </root>
   
</log4j:configuration>

src와 test 간의 차이는 마지막 부분의 <root> 안의 <priority>가 src에서는 warn이고 test에서는 info라는 것 뿐이 없습니다. 이 설정에서는 Log4J를 사용하고 있는데.. logback API를 이용해서 설정해도 당근 잘 동작합니다.

top


자바 System.out.println 콘솔 출력 가로채기

Java : 2010.01.25 16:49


public class Sout {

    public void hi(){
        System.out.println("hi");
    }

}

이렇게 콘솔에 어떤 메시지를 출력하는 경우가 있을 때 저걸 애플리케이션에서 캡춰할 수 있는 걸 만들어 보는 과제가 떨어졌다.

public class SoutTest {

    SoutInterceptor soutInterceptor = new SoutInterceptor();

    @Test
    public void sout() throws IOException {
        soutInterceptor.active();

        Sout sout = new Sout();
       sout.hi();

        assertThat(soutInterceptor.getMessages(), is("hi"));
    }

}

간단하게 테스트를 만들고 돌려보기 시작했다. 캡춰한 메시지를 어떻게 가져올지가 고민이었는데 그냥 생각난 가장 단순한 방법으로 가져오게했다. 이제 남은건 SoutInterceptor라는 녀석을 만드는 일이다. 뭘 어찌해야 한담 @_@;

가장 먼저 떠오른 방법은 콘솔을 모니터링 하는것이다. 그런데.. 넘 복잡할 것같고 막연하다. 다음으로 떠오른게 AOP. out.println()을 할 때 가로챌 수 있지 않을까? 하지만 힌트가 전달됐다. out을 교체할 수 있단다. 크헉.. 이건 뭐 거의 정답 수준의 힌트이지만 그렇게 간단하지는 않다고 한다. 좋아 해보자.

코딩은 구글신과 함께.. (또는 사부님 말씀대로 이클립스 코드를 뒤지면 나올지도 모른다. 사부님은 이미 뒤져본 것 같다. 자신이 생각한 방법과 동일한 방법을 사용했다고 한다. 어떤 건지는 안 찾아봐서 모르겠다;; 수천 수만 개나 되는 소스 코드를 받아오기도 귀찮고 그걸 IDE에 로딩하는데 엄청 오래 걸릴 것이며 잘못해서 뻑나거나 빌드가 안되고 컴파일 에러잡고 그러면서 삼천포로 가고 싶진 않았다.)

public class SoutInterceptor {

    private PipedInputStream pipedInputStream;
    private PrintStream originalPrint;

    public SoutInterceptor() {
        originalPrint = System.out;
        this.pipedInputStream = new PipedInputStream();
    }

    public String getMessages() throws IOException {
        byte[] messages = new byte[pipedInputStream.available()];
        pipedInputStream.read(messages, 0, messages.length);
        return new String(messages);
    }

    public void active() throws IOException {
        final PipedOutputStream pipedOutputStream = new PipedOutputStream(pipedInputStream);
        PrintStream saveStream = new PrintStream(pipedOutputStream) {
            @Override
            public void println(String x) {
                try {
                    pipedOutputStream.write(x.getBytes());
                } catch (IOException e) {
                    System.out.println("error");
                }
                originalPrint.println(x);
            }
        };
        System.setOut(saveStream);
    }

}

오호.. 잘 돌아간다.. +_+.

테스트를 좀 더 해보자.

        sout.hello();
       
        assertThat(soutInterceptor.getMessages(), is("hello"));

아래에 이렇게 추가하고 hello() 메서드 안에서는 hello를 출력하게 했다. 또 테스트가 잘 돌아간다. 흠.. 이제 맞게 한건가?

println(Stirng x)를 재정의 했는데 print(int), println(boolean) 를 호출할 때도 잘 동작한다. 왜그럴까?

top


차량 블랙박스 꼭 다세요;

모하니?/Watching : 2010.01.21 14:20




차량으로 높이뛰기 자해공갈자



후진 부부 사기단

세상이 흉흉하네요. @_@;; 저는 차가 없지만 아내가 임신하게되면 차를 리스할까 생각중이긴 합니다.
top


[봄싹 DevTerms] 활성화 맞춤형 개발

모하니?/Coding : 2010.01.21 10:15



현재 봄싹 DevTerms는 오픈을 한지 얼마 안되서 인지 사용자가 몇 안되고 쌓인 데이터가 별로 없지만 장차 사용자도 늘어나고 데이터가 많아지면 다음과 같은 기능을 추가할 계획입니다. (어제 오늘 오픈 하고 난 뒤 생간난 아이디어들입니다.)

봄싹 DevTerms 트위터가 활성화 되면..
- 개발 용어 및 한글 용어가 올라갈 떄마다 트위터에 등록되는데, 만약 봄싹 DevTerms 트위터가 활성화 되어 올라온 메시지에 누군가 reply나 retweet등을 달면 해당 메시지들을 읽어와서 봄싹 DevTerms에서 보여줄 수 있겠습니다.

개발 용어가 많이 등록되면..
- 오늘 용어, 이번주 용어 탭을 추가하여 오늘 하루에 몇 개나 등록됐으며 어떤 단어들이 등록됐는지 볼 수 있게 하겠습니다. 그러나.. 아직은 뭐..

등록된 개발 용어가 2000개가 넘으면...
- 아이폰과 안드로이드폰 용 애플리케이션을 개발하겠습니다.

개발 용어가 빈번히 등록 또는 수정 된다면..
- 코멧을 적용하여 트위터처럼 실시간으로 새로 등록된 용어가 있음을 알려주고 페이지 업데이트를 하라는 메시지를 전달하거나 자동으로 페이지를 업데이트 해주겠습니다. 리버스 Ajax를 적용해야되서 기술적으로도 재밌을 것 같네요.

등록자 외에도 개발 용어를 편집하고 싶은 분들이 있다면..
- 위키 형태로 히스토리를 저장하고 누가 언제 수정했는지 기록을 남기겠습니다. 현재는 관리자 권한을 가진 사용자는 모든 용어를 편집할 수 있지만 히스토리를 남기고 있지 않아서 위키라고 볼 수는 없는데 아직 이 기능이 필요한 시점은 아닌 것 같습니다. 활성화가 된다면 모를까..

뷰 카운트 및 추천 카운트가 1000이 넘는다면..
- 현재 봄싹 막내(스루)가 미래를 위한 대비 작업으로 천천히 진행 중이긴 한데, StackOverFlow 처럼 카운트 단위를 도입해서 1000건이 넘는 조회수에는 k를 붙여서 1000v -> 1kv로 카운트 단위를 적용하겠습니다.

The more you use it, the more you will get.



top


2010 대한민국 매쉬업 참가했습니다.

모하니?/Coding : 2010.01.20 16:30


"개발 용어 한글화 프로젝트 DevTerms"로 참가했습니다. 이 녀석을 어제 배포하고 밤새 나머지 주요 기능 중 하나였던 '관심 용어'까지 구현한 뒤 배포하고 참가 신청까지 완료했습니다. 2010 매쉬업 참가 목록에서 보실 수 있습니다.


위에 보시면 아시겠지만 작년 11월부터 이미 DevTerms를 만들고 싶다는 이야기를 봄싹 그룹스에서 했었습니다. 그러던 중 매쉬업 소식을 듣게 되었고 DevTerms에 오픈 API를 적용해서 내보내게 된 겁니다. 1석 2조가 됐지요. 어차피 봄싹에 새 기능으로 추가할 계획이었고 그 기능에 필요한 오픈 API를 적용해서 매쉬업까지 나갔으니 말이죠.

그래서 그런지 제가 제출한 제품은 매쉬업이 메인이 아니라 '개발 용어 한글화' 서비스가 메인입니다. (벌써 43개나 제출됐네요.. 캬오.)그걸 보조하는 수단으로 사전 API와 트위터 API 사용하고 있죠. 사전 API는 기대 했던 것 보다 효과가 좋다고 생각하고 있습니다. 개발 용어는 영어 사전으로 검색한 결과를 보여주고 한글 용어는 한글 사전으로 검색한 결과를 보여주고 있습니다. 트위터 API를 이용하여 등록되는 용어들을 트위터 메시지로 등록해주고 링크를 타고 봄싹 DevTerms로 올 수 있게 해뒀습니다. 트위터 서비스만 해두면 사용자들이 트위터 RSS 피드나 노티 애플리케이션을 이용해서 등록되는 용어들을 실시간으로 받아 볼 수도 있지요.

http://twitter.com/devterms

사실 중간에 매쉬업 참가를 포기할까도 고민했습니다. 혼자서 디자인까지 신경쓰면서 기능 구현을 하자니 중간에 탁탁 끊기는 느낌이고 누군가 제가 집중해서 기능을 구현하고 있으면 그 동안 만든 페이지들 디자인을 점검하고 개선해주면 좋겠다 싶어서 봄싹 팀원에게 도움을 요청했습니다. 다행히 그 친구가 흔쾌히 승낙하고 휴가까지 반납하고 우리 회사로 찾아와 개발을 해주었기 때문에 일정과 품질을 다 기대했던 것 만큼 맞출 수 있었다고 생각합니다. 회사나 계약 등으로 묶여서 같이 일하는 것이 아니라 순수한 열정으로 묶여서 '함께 일한다'는 것이 무엇인지 조금은 알 수 있을 것 같은 기분입니다.

개발에 투자한 일수는 얼마 되지 않습니다. 1월 초까지도 번역을 하고 있었고 JSF를 공부하던 중에 갑자기 매쉬업이 생각나서 저번주부터 오늘까지 한 10일 정도 달린 것 같습니다. 하지만 투자한 시간은 거의 한달 정도 회사일에 투자한는 시간이었던 것 같습니다. 아침 10시부터 새벽 5시까지 전철 타고 밥먹는 시간 뺴고 코딩만 해본건 처음이었습니다. @_@ (어제와 오늘은 출근 길에도 전철에서 코딩을 했습니다. 아흑.. '쟤 모야... '이런 사람들의 시선도 무시한 채 꾿꾿히..) 이렇게 빠져버린 날 이해해준 아내에게 그저 고맙고 미안할 뿐입니다.

원래 계획은 1월 20일. 오늘부터 다시 번역에 집중해서 2월 달 안에 번역을 마무리하는게 계획이었습니다. 그런데 왠걸.. 24일까지 수정할 수 있게 해준다는군요. 뭐 기존에 생각했던 기능들은 다 만들었지만 막상 써보니까 생기는 요구사항들을 무시할 순 없을 것 같습니다. 24일까지만 손을 대고 후딱 번역을 마무리 한 다음 3월부터 진정한 2010년을 맞이해야겠습니다. 후아...

결과가 어찌될지 몰겠지만 수고했다. 기선아. 조금만 더 하고 마무리하자.





top


개발 용어 한글화 프로젝트 "DevTerms"

모하니?/Coding : 2010.01.19 19:34


봄싹에서 진행하는 프로젝트 중 하나입니다. 저번 주부터 이녀석 개발하느라 번역도 안하고 회사일도 미루고 잠도 줄이면서 올인했습니다. 아직도 좀 더 만들어야 하는데 일단 사용할 수 있을 정도가 됐다는 판단하에 배포했습니다. (그런데 오판이었어요. 고치고 만들께 아직도 많네요. @_@)

이 프로젝트의 목적은 다음과 같습니다.

- 공동 역자들의 용어 통일화
- 개발 용어에 대한 이해
- 개발 용어 한글화에 대한 근거 확보
- 다수가 선호하는 한글 용어 식별
- 개발 용어 태그 정리

http://springsprout.org/term/index.do



사용해 보시고 의견 주세요~




top


2장 컴포넌트 식별자와 클라이언트 식별자

JSF/JSF in Action : 2010.01.13 15:21


- UI 컴포넌트는 서버에서는 컴포넌트 트리로 살고 클라이언트쪽에서는 여러 형태의 뷰 표현체로 존재한다.
- 서버쪽에서는 컴포넌트 식별자로 컴포넌트를 참조한다.
- 클라이언트에서는 클라이언트 식별자를 사용해서 컴포넌트를 참조한다.
- 컴포넌트 식별자를 이용해서 클라이언트 식별자를 생성한다.
- 컴포넌트 식별자는 반드시 문자 또는 언더바(_)로 시작한다.
- 컴포넌트 식별자를 생략하면 JSF가 자동으로 만들어 준다.
- Naming container: 하위 컴포넌트들이 모두 유일한 식별자를 가지고 있는 컴포넌트.
- 예를 들어 HtmlDataTable, HtmlForm의 하위 컴포넌트트 식별자에는 이 컴포넌트의 식별자를 이용해서 클라이언트 식별자를 만든다.

top


대중교통만 타면 짜증나게 만드는 이 더러운 세상!!!!

모하니?/Thinking : 2010.01.12 23:20


아무리 좋은 얘기도 반복해서 들으면 짜증나는데... 아내가 대중교통 이용하면서 짜증나고 시비붙는 얘기를 듣다가 스트레스 폭파 직전이다. 하루 이틀도 아니고..  미치겠다. @_@;;

기존의 MVC 프레임워크가 짜증나면 쓰지 말던가, 새로운 MVC 프레임워크를 만들던가, 이도 정도 아니면 그냥 참고 쓰던가. 셋 중 하나 아닌가?

짜증난다고 시비붙고 욕해봤자 돌아오는 건? 스트레스. 바뀌는 건? 없다. 스트레스는 부부라는 레퍼런스를 타고 나에게 전달되고 아내가 가지고 있던 스트레스도 고대로 위임을 통해 나에게 전달된다. '전달'을 통해서 아내의 스트레스는 어느정도 풀렸는지도 모르겠다. 하지만 나는 이게 왠 봉변이란 말인가? 부부니까 이런것도 받아들여야 하는게 당연한거긴 하겠지만 그래도 나는 비생산적인 스트레스를 전달받고 싶은 마음이 추호도 없다.

피하고 싶으면 피하고, 바꾸고 싶으면 바꿔라. 둘 다 아니면 그냥 참자.

그래도 꼭 '전달'이라는 수단으로 스트레스를 풀어야겠다면 나 말고 다른 사람에게 전달하던지, '여보 영어 공부 좀 더 열심히 해서 우리 빨리 한국을 떠나기로 해요', '여보 내가 빨리 영어 알려줄께' 라고 다소 생산적인 것으로 포장해서 전달해주면 좋겠다.

top


[스프링 3.0] JSR-330 Provider 인터페이스 이용해서 싱글톤이 아닌 빈을 싱글톤 빈에 주입하기

Spring/3.0 : 2010.01.12 22:26


결국 어디선가는 룩업을 해야합니다. 그냥 주입해서 될 일이 아닙니다. getter injection을 사용할 수도 있지만 AOP, Proxy 등 다소 장황해집니다. 룩업은 하되 가장 표준적이면서, 편리하고, 써드파티 라이브러리 의존성을 낮추는 방법을 얼마전 사부님 블로그를 통해 알게 되었습니다. 그 방법은 바로 구글 쥬스에 있던 걸 표준화 한 JSR-330의 Provider.

먼저 빈 설정입니다. 빈 두개. White는 싱글톤, Ship은 프로토타입 스코프로 설정합니다.

@Configuration
public class ProviderTestAppConfig {

    @Bean White getWhite(){
        return new White();
    }

    @Bean @Scope("prototype") Ship getShip(){
        return new Ship();
    }
}

다음은 위에 등록한 실제 빈 클래스들..

public class Ship {
}

public class White {

    @Autowired
    private Provider<Ship> shipProvider;

    public void hi(){
        System.out.println(shipProvider.get());
    }

}

바로 이 부분이 가장 눈여겨 봐야 할 코드입니다. @Autowired 대신에 @Inject를 써도 됩니다. 그게 중요한게 아니라 스프링이 Provider 인터페이스 구현체를 자동으로 만들어 주입해준다는 것이 중요합니다. 어떤 클래스가 어떻게 해주는지는 귀찮아서 찾아보지 않았습니다. 사부님 책에는 자세한 설명이 나올지도?! +_+

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(loader = AnnotationContextLoader.class)
public class ProviderTest {

    @Autowired White white;

    @Test
    public void get(){
        white.hi();
        white.hi();
        white.hi();
        white.hi();
    }

}


이건 뭐 기본적으로 자바5는 사용한다는 전제하에 제공되는 것이기 때문에 자바5 도입이 불가능하거나 거부감이 있으신 분들은 스프링의 ObjectFactroy나 ServiceLocatorFactoryBean를 검토해보시는게 좋겠습니다.

ps1: 빈 설정에서 @Scope("prototype")을 때어내도 같은 결과가 나오지 않을까 궁금하다는 생각을 하신 분이 계신가요?? 정답은... 안갈챠드려요. 직접 해보세요. 캬캬캬캬.

ps2: AnnotationContextLoader 이 클래스는 저랑 성윤이가 만든거고 스프링에 없습니다. http://jira.springframework.org/browse/SPR-6567 여기서 소스 및 테스트를 받으실 수 있답니다. vote 좀;;

ps3: 근데 이런 글에 누가 관심이나 있나요.. 싱글톤만 쓰는데;; @_@; 싱글톤이 아닌 빈을 어떻게 왜 써먹는걸까요..? 논의는 봄싹 그룹스에서. 음하핫
top


2장 JSF 요청 처리 생명주기 - Phase 6. 응답 보여주기

JSF/JSF in Action : 2010.01.11 20:58


Render Response

- 최종적으로 사용자에게 응답을 보낸다.
- 뷰 상태를 저장하여 '뷰 복원하기' 단계에서 재사용할 수 있게 한다.
- 특정 뷰 기술에 종속적이지 않다.
- 컨트롤러의 인코딩 메서드 결과만을 뷰에서 사용할 수 있다.
- 컨트롤러의 인코딩 메서드 결과를 마크업을 만드는 애플리케이션에서 통합할 수 있다.
- 컨트롤러의 인코딩 메서드 결과를 정적인 템플릿에 통합할 수 있다.
- 컨트롤러의 디코딩 메서드 결과를 JSP 같은 동적 리소스에 통합할 수 있다.
- 각각의 컨트롤러 인코딩 과정 중에 변환기(Conveter)를 사용하여 컴포넌트의 값을 문자열로 변환한다.
- 이 단계가 JSF 요청 처리 생명주기의 마지막 단계다.
top


2장 JSF 요청 처리 생명주기 - Phase 5. 애플리케이션 호출하기

JSF/JSF in Action : 2010.01.08 21:49


Invoke Application

- 이 단계에서 등록되어 있는 리스너로 이벤트를 전파한다.
- '요청값 적용하기' 단계에서 만든 액션 이벤트를 여기서 전파한다.
- 컴포넌트에 등록된 액션 리스너 EL을 평가하여 백빈의 특정 메서드(액션 리스너 메서드)를 실행한다.
- 액션 리스너 메서드는 컴포넌트의 actionListener 속성으로 설정한다.
- 액션 리스너 메서드 실행이 끝나면 모든 컴포넌트에 자동으로 등록되는 기본 리스너를 실행한다.
- 기본 리스너는 컴포넌트에 등록되어 있는 액션 메서드를 호출한 뒤 해당 메서드의 논리적인 이름을 통해 랜더링할 뷰를 찾는다.
- 액션 메서드는 컴포넌트의 action 속성을 설정한다.
- 액션 메서드가 없는 경우 기본 액션 리스너가 현재뷰를 랜더링 한다.
- 액션 리스너 메서드나, 액션 메서드에서는 직접 응답을 랜더링 할 수도 있고, 애플리케이션 메시지를 추가할 수도 있고, 이벤트를 만들 수도 있고, 애플리케이션 로직을 실행할 수도 있고, 그냥 뷰 랜더링 단계로 건너뛸 수도 있다.
- 모든 리스너 실행이 끝나면 뷰를 랜더링 할 준비가 된 것이다.


top


새해 선물 또치



아내가 선물해준 신년 선물 또치.
이쁘구나!
근데 요즘 말썽을 부려서 원;;;



아침에 일어나서 제일 먼저하는 일이 이녀석 똥/오줌을 치우는 일이라는 @_@;;
top

TAG 또치

2장 JSF 요청 처리 생명주기 - Phase 4. 모델 값 변경하기

JSF/JSF in Action : 2010.01.07 20:38


Update Model Values

- JSF EL 표현식을 통해서 모델 또는 백빈의 속성 값을 설정한다.

<h:inputText id="helloInput" value="#{helloBean.numControls}" 
             required="true"> 
... 
</h:inputText> 

1. helloBean이라는 키를 가지고 있는 빈을 찾아온다.
2. helloBean의 numControls 속성에 값을 해당 컴포넌트의 로컬 값으로 설정한다.

- 이 단계가 끝나면 관련 리스너에 이벤트를 보내고, 여태까지 그랬던것처럼 리스너에서는 응답을 직접 출력하거나  Render Response 단계로 건너뛸수도 있다.



top


2장 JSF 요청 처리 생명주기 - Phase 3. 검증하기

JSF/JSF in Action : 2010.01.07 20:26


Process Validation

- 이 단계에서 JSF는 컴포넌트 트리를 순회하면서 각각의 컴포넌트에게 자신의 값을 수용할 수 있는지 확인하도록 요청한다.
- '요청 값 적용하기' 단계에서 입력 컴포넌트들이 갱신되기 때문에 이 단게에서는 사용자가 입력한 최신값을 가지고 있다.
- 검증을 하기전에 변환기에 의해 값이 변환된다.
- 변환기와 검증기를 무사히 거치고 나면 해당 값을 서브밋하고 다음 단계로 넘어간다.
- 그렇치 않은 경우 '응답 보여주기' 단계로 넘어간다.

<h:inputText id="helloInput" value="#{helloBean.numControls}" 
             required="true"> 
<f:validateLongRange minimum="1" maximum="500"/> 
</h:inputText> 

1. required 속성이 true가 입력한 값이 있는지 검사한다.
2. 컨버터가 입력한 값을 int로 바꾼다.
3. validateLongRange 검증기가 값의 범위를 검증한다.

- submitted value가 변환과 검증이 끝나면 그 값을 기반으로 로컬 값을 세팅하는데 이때 value change event가 발생하고 그에 따른 적절한 리스너가 실행된다. 이 리스너들에 의해 직접 응답을 출력하거나 Render Repsponse 단계로 넘어갈 수 있다.
- 모든 submitted value가 유효하다면 다음 단계로 넘어간다.


top


2장 JSF 요청 처리 생명주기 - Phase 2. 요청 값 적용하기

JSF/JSF in Action : 2010.01.06 20:42


Apply Request Values

- 사용자 입력을 받는 컴포넌트들은 사용자가 입력한 원래 데이터를 나타내는 submitted value를 가지고 있다.
- 프레임워크가 요청의 매개변수를 기반으로 submitted value를 설정해준다.
- 이 과정을 decoding이라 부른다.
- 각 컴포넌트는 자신의 id와 자신을 감싸고 있는 컴포넌트의 id를 이용해서 client identifiter를 만든다.
- 이 단계에서 컴포넌트의 요청에 자신의 client id에 해당하는 매개변수가 있는지 확인하여 있다면 그 매개변수의 값으로 설정한다.
- 컴포넌트의 immediate 속성을 true로 설정하면 검증 단계에서 하지않고 그 즉시 검증을 실행한다.
- 이 단계에서 액션 이벤트를 만들 수도 있다. 만들어진 이벤트는 FacesContext에 추가되고 나중에 '애플리케이션 호출' 단계에서 사용된다.
- 이 단계가 끝난뒤 존재하는 이벤트를 관련 리스너로 전달한다.
- 모든 랜더러, 컴포넌트, 리스너는 생명주기를 단축시켜 이 단계를 건너뛰거나 바로 최종단계로 넘어갈 수도 있다.
- 또는 각각의 입력 컴포넌트가 적절하게 디코딩된 속성을 가지고 현재 요청에 기반한 최신 값을 가지고 있을 것이다.



top


2장 JSF 요청 처리 생명주기 - Phase 1. 뷰 복원하기

JSF/JSF in Action : 2010.01.05 21:37


Restore View

- 뷰는 특정 페이지를 구성하는 모든 컴포넌트를 나타낸다. 
- 브라우저 위에 히든 필드를 이용해서 클라이언트에 저장될 수도 있고, 세션을 이용해서 서버에 저장될 수도 있다. 서버에 저장되는게 기본값이다.
- 각각의 뷰는 컴포넌트 트리로 구성되며 고유의 식별자를 가지고 있다. 그 식별자를 "뷰 식별자"라고 한다. View identifier
- 뷰 식별자는 요청에서 특수한 경로에 해당한다. 서블릿 경로 이후의 값이 뷰 식별자가 된다. 예) http://spirngsprout.org/study/35.do 에서 /study/35.do
- 폼을 보여준 페이지와 동일한 페이지로 다시 포스팅 요청을 하는 것을 포스트백(postback)이라고 한다.
- 기존의 프레임워크와 차이: "이 액션을 수행하라." VS "사용자가 이 명령을 실행했다.", "사용자가 이 컨트롤의 값을 변경했다."
- 이때 중요한 것은 최종 페이지가 어떤 것이며, 그곳에서 어떤 이벤트가 발생했는냐 이다.
- 현재 뷰를 찾아서 사용자가 입력한 내용을 거기에 적용하는 것이 이 단계의 주요 작업 중 하나이다. 
- 세션에서 뷰를 찾아 본 뒤에 뷰가 있으면 그것을 가져와서 FacesContext에 저장하고, 없으면 새로 요청한 뷰 식별자를 기반으로 새로운 뷰를 만들어서 FacesContext에 저장한다.
- 뷰를 복원하는 단계에서 컴포넌트의 값은 물론이고 그와 관련된 이벤트 리스너, 검증기, 변환기도 복원됐는지 확인한다.
- 백빈의 속성에 바인딩되어 있는 컴포넌트의 경우에는 이 단계에서 백빈의 속성과 컴포넌트가 동기화된다.
- 브라우저가 보낸 HTTP 요청의 헤더를 통해 뷰에 사용할 언어를 설정한다.
- 요청이 포스트백일 경우에는 다음 단계로 넘어가고, 그외의 경우에는 처리할 사용자 입력이 없기 때문에 응답 랜더링(Render Response) 단계로 넘어간다.

 
top


시베리안 시츄 - 또치





시베리안 애완 암컷 시츄.. 또치야.. 니가 고생이 많다.
top

TAG 또치, 시츄

2장 JSF 기본 - 요청 처리 생명주기

JSF/JSF in Action : 2010.01.04 18:11


뷰 복원하기(restore view) - 선택한 뷰에대한 컴포넌트 트리를 찾거나 만드는 단계.  HtmlCommandButton 같은 컴포넌트에서 액션 이벤트를 발생 시킨다.

요청 값 적용하기(Apply Request Values) - 컴포넌트의 값들을 요청에 담겨있는 값과 동일하게 맞춘다. 이 과정에서 변환기(Converter)가 사용될 수 있으며,  변환시 에러가 발생하면 변환 에러를 추가한다.

검증 수행(Process Validations) - 모든 컴포넌트가 각자 검증을 수행하도록 지시한다. 검증 에러 메시지가 보고될 수 있다.

모델 값 갱신(Update Model Values) - 컴포넌트에 연결된 백빈(backing bean) 또는 모델 객체의 값을 갱신한다.  변환 에러가 보고 될 수 있다.

애플리케이션 호출(Invoke Application) - 등록된 액션 리스너를 호출한다. 

응답 랜더링(Render Response) - 선택한 뷰를 보여준다.



top