SimpleFormController 에피소드2

아래 발생한 문제는 SimpleFormController와는 전혀 관계가 없는 내용일 수도 있다는 것을 미리 알려드리고 시작해야겠습니다. 이유는 결론을 보시면 알게 되실듯.

사용자 삽입 이미지위와 같은 리스트가 있습니다. 오호 분명히 “기선”이라는 사람이 매우 많이 있습니다. 왼쪽에 있는 search로 이동해서 “기선”을 찾아 봅시다.

사용자 삽입 이미지이렇게 기선을 입력하고 enter 또는 search 버튼을 클릭했더니 다음과 같은 화면이 출력 됐습니다.

사용자 삽입 이미지헐…출력할 것이 없다니.. 도대체 어디서 문제가 생긴거야???

1. 소스코드를 점검하기 시작합니다. JSP와 Controller를 점검하기 시작했습니다.
[#M_ more.. | less.. |

 컨트롤러

public class SearchMemberController extends SimpleFormController {

    private MemberRepository memberRepository;

    public SearchMemberController() {
        setCommandName(“memberCommand”);
        setCommandClass(MemberCommand.class);
        setFormView(“search”); // 생략가능.
        setSuccessView(“searchResult”);
    }

    public void setMemberRepository(MemberRepository memberRepository) {
        this.memberRepository = memberRepository;
    }

    @Override
    protected ModelAndView onSubmit(Object command) throws Exception {
        MemberCommand searchingMember = (MemberCommand) command;
//        System.out.println(“message 출력: ” + search.getName());
        ModelAndView mav = new ModelAndView(getSuccessView())
            .addObject(“resultMember”, memberRepository.findByName(searchingMember.getName()))
            .addObject(“memberCommand”, searchingMember);
        return mav;
    }
}

search.jsp

<jsp:root version=”1.2″ xmlns:jsp=”http://java.sun.com/JSP/Page”
    xmlns:display=”urn:jsptld:http://displaytag.sf.net”
    xmlns:form=”http://www.springframework.org/tags/form”>
    <jsp:directive.page contentType=”text/html; charset=UTF-8″ />
    <jsp:include page=”inc/header.jsp” flush=”true” />

    <html>
    <head>
    <title>Search For Members</title>
    </head>
    <body>
    <h2>Search For Members</h2>
    <form:form commandName=”memberCommand” method=”post”>
        <table>
            <tr>
                <td>Name:</td>
                <td><form:input path=”name” /></td>
            </tr>
            <tr>
                <td colspan=”2″><input type=”submit” value=”Search” /></td>
            </tr>
        </table>
    </form:form>
    </body>
    </html>

    <jsp:include page=”inc/footer.jsp” flush=”true” />

</jsp:root>

searchResult.jsp

<jsp:root version=”1.2″ xmlns:jsp=”http://java.sun.com/JSP/Page”
    xmlns:display=”urn:jsptld:http://displaytag.sf.net”
    xmlns:c=”http://java.sun.com/jstl/core_rt”>
    <jsp:directive.page contentType=”text/html; charset=UTF-8″ />
    <jsp:include page=”inc/header.jsp” flush=”true” />

    <jsp:scriptlet>
        java.util.List members = (java.util.List)request.getAttribute(“resultMember”);
        request.setAttribute(“test”, members);
    </jsp:scriptlet>

    <h2>Search Results</h2>

    <display:table name=”test” export=”true”>
        <display:column property=”name” title=”이름”/>
        <display:column property=”email” autolink=”true”/>
        <display:column property=”phone” title=”연락처”/>
        <display:column property=”blogAddress” title=”Blog” autolink=”true”/>
        <display:column property=”messengerId” title=”MSN”/>
    </display:table>

    <jsp:include page=”inc/footer.jsp” flush=”true” />

</jsp:root>

_M#]
처음에는 command 객체에 값이 들어가지 않는 것 같다는 생각이 들었습니다. 중간에 출력문을 한번 넣어보면 될 것을[footnote]소스코드에 주석처리 된 부분입니다.[/footnote] 아직까지도 SimpleFormController가 어떻게 동작하는 것인지 제대로 모르기 때문에 그 떈 더욱 더 Command 객체에 값이 언제 들어가는 것인지 알 수가 없었습니다.

그리고 두번째는 지난 글에서 의문이 들었던 command 객체를 도메인 객체로 써야 하는건가? 싶어서 Member로 바꿔서도 해봤습니다. 결과는… 여전했습니다.

2. 조금더 공부하다가 Command 객체에 값이 들어갔는지 확인해봤습니다. 세상에 값이 들어가 있었습니다. 값이 들어가 있긴 한데… 한글이 깨져있더군요.

message 출력: ?¸°???

-_-;; 난감했습니다. 이전에 DB 인코딩 문제로 한참을 해맸었는데 또다시 인코딩문제인가.. ㄷㄷㄷ 거리고 있었는데 다행히 한수형이 알려주셔서 web.xml에 다음과 같이 코드를 넣어줬더니 한글이 제대로 보였습니다.
[#M_ more.. | less.. |     <filter>
        <filter-name>encodingFilter</filter-name>
        <filter-class>
            org.springframework.web.filter.CharacterEncodingFilter
        </filter-class>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>UTF-8</param-value>
        </init-param>
        <init-param>
            <param-name>forceEncoding</param-name>
            <param-value>true</param-value>
        </init-param>
    </filter>

    <filter-mapping>
        <filter-name>encodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>_M#]
3. 좋았습니다. 이제 콘솔에 찍어보니 한글도 제대로 보이고 검색을 날렸는데… 여전히!! 못찾는겁니다. 왜?? 도대체 왜!! 못찾는거야.. 하면서 iBATIS에 SQL을 확인하러 갔습니다… 세상에…ㅠ.ㅠ
[#M_ more.. | less.. |     <!– find –>
    <select id=”find” resultMap=”memberMap”>
        SELECT id, name, messengerId, email, blogAddress, phone
        FROM Member
        WHERE
        <isNotNull property=”phone”>phone = #phone#</isNotNull>
        <isNotNull property=”blogAddress”>blogAddress = #blogAddress#</isNotNull>
        <isNotNull property=”email”>email = #email#</isNotNull>
        <isNotNull property=”messengerId”>messengerId = #messengerId#</isNotNull>
        <isNotNull property=”name”>name = #name#</isNotNull>
    </select>_M#]like로 바꿔야 겠어요. 흐흐흐흐흐흐흐

SimpleFormController 에피소드1

이녀석을 공부하는 어제부터 현재까지 여러 가지 에피소드들이 있었습니다. 발생한 의문들에 자문 자답을 해봅니다.

1. SimpleFormController가 어떻게 흘러가는 건지 이해가 되지 않았습니다.

AbstractController는 ModelAndView 객체에 요청을 처리할 view이름과 그 view에서 사용할 객체를 전달해 주었습니다. 그런데 이녀석은 그렇게 처리하기엔 뭔가.. 허전하다고 느껴지거든요. 자신에게 어떤 요청이 들어왔다는 것은 무엇을 입력할 곳을 찾아 왔다는 것이고[footnote]검색을 하기 위한 링크를 클릭 했다든가 회원 가입, 글 쓰기등의 버튼을 클릭했을 것입니다.[/footnote] 그럼 일단 그 화면으로 이동을 해줘야 하고 그 때 사용자가 입력할 데이타를 받아들일 객체가 있어야겠습니다. 아마도 그녀석을 Command 객체라고 부르는 것 같습니다. 일단 여기까지는 AbstractController와 비슷합니다. 하지만 여기서 끝나면 안되겠죠. 사용자가 입력을 마치고 엔터를 쳤을 때 또 다시 요청이 날아오게 됩니다. 이것도 역시 처리를 해줘야겠죠. 이 요청도 처리를 합니다. 생성자에 setSuccessView(“결과를 보여줄 view 이름”) 을 생성자에 추가해주면 그쪽 페이지로 이동하게 됩니다.

1.1. Command 객체는 뭔가?

도메인 객체인가? 아닌것 같다는 생각이 듭니다. 물론 도메인 객체를 써도 되겠지만 Command 객체는 사용자가 입력한 값을 받아 오는 역할을 하는 객체이고 만약 사용자가 입력하는 정보다 여러개의 객체에 걸쳐있는 값들이라면 그 때는 도메인 객체만으로는 처리할 수 없겠죠. 아마 그럴 땐 Command 객체를 하나 만들어서 그 안에서 해당 객체에 정보가 들어가도록 처리를 해야될 것 같습니다.

1.2. ModelAndView 객체에는 여러 객체가 들어갈 수 있는 건가?

add(“이름”, 객체) 형태의 메소드로 여러 객체를 ModelAndView에 붙여 줄 수 있습니다. 그리고 여기서 붙인 객체들은 “첫번째 요청에 의해 보여지는 페이지”와 “두번째 요청에 의해 보여지는 페이지”[footnote]success view에서 요청한 페이지[/footnote]에서 모두 사용이 가능합니다.

Spring’s form tag

참조 : http://static.springframework.org/spring/docs/2.0.x/reference/mvc.html#mvc-formtaglib

JSP 맨 위에

<%@ taglib prefix=”form” uri=”http://www.springframework.org/tags/form” %>

이렇게 올려놓고…

<form:form commandName=”searchMember”>
    <table>
        <tr>
            <td>Name:</td>
            <td><form:input path=”name” /></td>
        </tr>
     </table>
</form:form>

요론식으로 쓰면 된다길래 해봤는데…다음과 같은 에러 메시지 나옵니다.

org.apache.jasper.JasperException: /search.jsp(3,5) Invalid standard action

맨위에 올려놓은 <%@ taglib ..%> 이 부분을 없애면 페이지를 읽긴하는데 저게 없으니까 Spring form tag를 쓸 수가 없고(이걸 써보려고 한건데 못쓰게 되면 안되는뎅;) 저걸 붙이면 에러가 나오고.. 흠.. 왜이러나.. ㅠ.ㅠ

아래는 search.jsp 코드 전부 입니다.
[#M_ more.. | less.. | <%@ taglib prefix=”form” uri=”http://www.springframework.org/tags/form”%>

<jsp:root version=”1.2″ xmlns:jsp=”http://java.sun.com/JSP/Page”
    xmlns:display=”urn:jsptld:http://displaytag.sf.net”>
    <jsp:directive.page contentType=”text/html; charset=UTF-8″ />
    <jsp:include page=”inc/header.jsp” flush=”true” />

    <html>
    <head>
    <title>Search For Members</title>
    </head>
    <body>
    <h2>Search For Members</h2>
    <form:form commandName=”searchMember”>
        <table>
            <tr>
                <td>Name:</td>
                <td><form:input path=”name” /></td>
            </tr>
        </table>
    </form:form>
    </body>
    </html>

    <jsp:include page=”inc/footer.jsp” flush=”true” />

</jsp:root>
_M#]
헉.. 포스팅 하자마자 문제 해결…

jsp 1.2  문법을 보니깐 xml에서는 taglib을 <jsp:root xmlns:이름=”uri” >이렇게 지정해 주고 쓰는 거군요.

위에 있는 코드 윗 부분을 아래 처럼 수정을 하면 제대로 tag가 먹힙니다. 나이스!!  🙂

<jsp:root version=”1.2″ xmlns:jsp=”http://java.sun.com/JSP/Page”
    xmlns:display=”urn:jsptld:http://displaytag.sf.net”
    xmlns:form=”http://www.springframework.org/tags/form“>

스타크래프트 리플레이(로템)

주말에 스터디가 끝나고 가끔 스타 팀플을 하는데 쉽지 않습니다. ㅠ.ㅠ 프로토스 유저인데 어떻게 하면 좀 더 잘 할까 하다가 리플레이를 보고 따라하는게 가장 좋을 것 같다는 생각에 친구가 알려준 스타크래프트 관련 사이트에서 리플레이들을 찾았습니다. ㅎㅎ

http://pgr21.com/
http://www.ygclan.com/
http://wc3.gosugamers.net/

위 세곳에서 찾았으며 프로토스가 이기고 맵은 Lost Temple에서 치뤄진 경기만 찾았습니다.

P vs P

bm248.rep둘 다 드라곤 테크 타고 한 명은 옵저버 한 명은 리버를 먼저 뽑고 치열한 공방이 벌어집니다.

P vs T

bm249.rep12시 테란 3시 프토 일 때 초반 일꾼으로 입구를 못막게 하고 초반에 끝장을 봅니다.

bm250.rep아비터를 5~6개 활용해서 테란의 물량을 간단하게 제압합니다.

P vs Z

bm251.rep초반에 프토가 거의 gg 직전까지 갑니다. 그러다가 역전!! 한시간이 넘는 장기전인데 다크아콘 두 마리 하이템플러 두 마리로 디바우러 한 부대를 잡아버립니다. 이 밖에도 프토가 확장하는 방법이나 타이밍등 저그 상대로 할 때 배울 것이 많은 리플입니다.

bm252.rep초반 질럿을 많이 뽑아서 압박하니까 상대는 뮤탈을 빨리 뽑습니다. 그 뮤탈을 아콘으로 막고 승립니다.
bm253.rep앞마당 가져가는 저그 상대로 더블 넥서스를 합니다. 프로브 정찰을 열심히 해서 포톤 캐논을 무리하게 짓지 않는 모습이 인상깊었습니다.

사용자 삽입 이미지

Self Number 찾기

대엽님 블로그에서 퀴즈를 발견하고 원문을 보고 풀었는데 원문에는 파이썬 코드인지 모르는 문법이라 그런가 눈에 잘 안들어오네요. 대엽님이 푼 코드는 자바여서 역시 눈에 잘 들어오더군요. 문제를 다르게 풀 수 있을 것 같아서 코딩을 해봤습니다.

저는 1~4999까지 배열을 만들고 Generated 숫자가 될 수 있는 것들[footnote]다른 말로 generator를 가지고 있는 수[/footnote]을 지워나갔습니다.[footnote]해당 index안에 들어있는 수를 0으로 만들었습니다.[/footnote] 지우고 남는 숫자들이 Self Number들이기 때문에 나중에 배열에 들어있는 수를 쫙 더해주면 됩니다.

[#M_ more.. | less.. | public class SumOfSelfNumber {
    public static void main(String[] args) {
        new SumOfSelfNumber().start();
    }

    private void start() {
        int[] numbers = makeArray();
        checkSelfNumber(numbers);
        System.out.println(addAllSelfNumber(numbers));
    }

    private int[] makeArray() {
        int[] numbers = new int[4999];
        for (int i = 1; i < 5000; i++) {
            numbers[i-1] = i;
        }

        return numbers;
    }

    private void checkSelfNumber(int[] numbers) {
        for (int i = 0 ; i < numbers.length ; i++) {
            int generatedNumber = 0;
                generatedNumber = generate(i+1);
            if (generatedNumber != 0 && generatedNumber < 5000) {
                numbers[generatedNumber – 1] = 0;
            }
        }
    }

    private int generate(int number) {
        char[] chars = new String(number+””).toCharArray();
        int[] ints = new int[chars.length + 1];
        ints[0] = number;
        for (int j = 1; j < ints.length; j++) {
            ints[j] = (int)chars[j-1] – 48;
        }

        int generatedNumber = 0;
        for (int i = 0; i < ints.length; i++) {
            generatedNumber += ints[i];
        }

        return generatedNumber;
    }

    private long addAllSelfNumber(int[] numbers) {
        long sum = 0;
        for(int i : numbers)
            sum += i;
        return sum;
    }
}_M#]
코딩할 때 불편한 점은 int -> char -> int 이 과정에서 숫자가 변질 되는데 이것좀 어떻게 편하게 바꿀 수 있는 방법이 없을지 궁금하네요.

대엽님 코드를 보니 (int)c – (int)‘0’ 이런 방법으로  char 안에 들어간 int값을 그대로 살려냈는데 저도 마찬가지로 (int)chars[j-1] – 48 이런식으로 int값을 살려냈습니다. 좋은 방법 아시는 분~