본문 바로가기

WINK-(Web & App)/Spring Boot 스터디

[2025 1학기 스프링 부트 스터디] 남윤찬 #7주차

반응형

API 메시지 바디

단순 텍스트

messageBody에 단순 텍스트를 보내게 되면 content-type이 text/plain으로 설정되어 요청이 넘어온다.

protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        ServletInputStream inputStream = request.getInputStream();
        String messageBody = StreamUtils.copyToString(inputStream, StandardCharsets.UTF_8);

        System.out.println("messageBody = " + messageBody);

        response.getWriter().write("ok");
    }

서블릿 라이브러리의 ServletInputStream과 StreamUtils를 사용해 byte로 된 전달 받은 텍스트를 문자표(Charset)를 사용해 String으로 파싱해준다.

JSON

json을 사용하는 방식은 http api에서 주로 사용한다. 이때는 content-type이 application/json으로 설정되고,messageBody가 json 형식으로 작성된다. messageBody = {"username": "hello", "age": 20}

private ObjectMapper objectMapper = new ObjectMapper();

    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        ServletInputStream inputStream = request.getInputStream();
        String messageBody = StreamUtils.copyToString(inputStream, StandardCharsets.UTF_8);

        System.out.println("messageBody = " + messageBody);
        HelloData helloData = objectMapper.readValue(messageBody, HelloData.class);

        System.out.println("helloData.getUsername() = " + helloData.getUsername());
        System.out.println("helloData.getAge() = " + helloData.getAge());

        response.getWriter().write("ok");
        
    }

json은 텍스트를 파싱하는 것과는 다르게 잭슨이라는 json 파싱 라이브러리의 ObjectMapper를 사용해 미리 만둘어둔 객체의 형식에 맞추어 json을 객체로 파싱하여 사용하게 된다. 물론 json 또한 문자열이기 때문에 String으로도 파싱할 수 있다.

HTTP 응답

HttpRequestServlet과 유사하게 HttpResponseServlet은 응답 메시지를 만드는 역할을 한다. http 응답코드를 지정하고, 헤더와 바디를 생성하고, content-type, 쿠키, redirect 등의 기능을 한다.

단순 텍스트, HTML

단순 텍스트는 앞에서 이미 나왔듯이 writer.println()을 사용해 내용을 작성해 response 내용을 만들어낼 수 있다. 다만 html로 전송하고 싶다면 content-type을 text/html로 정해줘야한다.

@Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // Content-type: text/html;charset=utf-8
        response.setContentType("text/html");
        response.setCharacterEncoding("utf-8");

        PrintWriter writer = response.getWriter();
        writer.println("<html>");
        writer.println("<body>");
        writer.println("  <div>안녕?</div>");
        writer.println("</body>");
        writer.println("</html>");
    }

API JSON

json을 반환할 때는 content-type을 application/json으로 지정하고, 요청을 처리할 때와 비슷하게 잭슨 라이브러리가 제공하는 ObjectMapper의 writeValueAsString() 메서드를 사용해 객체를 json 문자로 변경한다.

@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    // Content-Type: application/json
    response.setContentType("application/json");
    response.setCharacterEncoding("UTF-8");

    HelloData helloData = new HelloData();
    helloData.setUsername("kik");
    helloData.setAge(18);

    // {"username": "kik", "age": 18}
    String result = objectMapper.writeValueAsString(helloData);
    response.getWriter().write(result);

}

+추가적으로, application/json은 스펙상 utf-8을 사용하도록 되어있어서 charset=utf-8과 같은 추가 파라미터를 지원하지 않아서 따로 작성하지 않아도 된다.


서블릿, JSP, MVC 패턴

회원 관리 웹 애플리케이션 요구사항

회원 정보: 이름(username), 나이(age)

기능 요구사항: 저장, 목록 조회

이 정도 간단한 기능을 서블릿, JSP, MVC 패턴 모델 순서대로 구현하며 어떻게 변화해왔는지 알아본다.

서블릿으로 만들기

회원 객체와 리포지토리를 생성한다. 리포지토리는 싱글톤 패턴에 주의하여 설계한다.

/**
 * 동시성 문제가 고려되어 있지 않음, 실무에서는 ConcurrentHashMap, AtomicLong 사용 고려
 */
public class MemberRepository {
    private static Map<Long, Member> store = new HashMap<>();
    private static Long sequence = 0L;

    private static final MemberRepository instance = new MemberRepository();

    public static MemberRepository getInstance() {
        return instance;
    }

    private MemberRepository() {}
    
    ...
}

회원을 저장할 수 있는 간단한 폼을 아래와 같이 Servlet을 사용해 만들어준다.

그리고 폼을 전송하면 이를 처리하도록 파라미터를 읽어낸다.

@WebServlet(name = "memberSaveServlet", urlPatterns = "/servlet/members/save")
public class MemberSaveServlet extends HttpServlet {

    private MemberRepository memberRepository = MemberRepository.getInstance();

    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("MemberSaveServlet.service");
        String username = request.getParameter("username");
        int age = Integer.parseInt(request.getParameter("age"));

        Member member = new Member(username, age);
        memberRepository.save(member);
        
        ...
    }
}

그리고 회원 목록을 띄워주는 서블릿을 작성했습니다.

@WebServlet(name = "memberListServlet", urlPatterns = "/servlet/members")
public class MemberListServlet extends HttpServlet {

    private MemberRepository memberRepository = MemberRepository.getInstance();

    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        response.setContentType("text/html");
        response.setCharacterEncoding("UTF-8");

        List<Member> members = memberRepository.findAll();

        PrintWriter w = response.getWriter();
        w.write("<html>");
        w.write("<head>");
        w.write("   <meta charset=\"UTF-8\">");
        w.write("   <title>Title</title>");
        w.write("</head>");
        w.write("<body>");
        w.write("<a href=\"/index.html\">메인</a>");
        w.write("<table>");
        w.write("   <thead>");
        w.write("   <th>id</th>");
        w.write("   <th>username</th>");
        w.write("   <th>age</th>");
        w.write("   </thead>");
        w.write("   <tbody>");
        /*
        w.write(" <tr>");
        w.write(" <td>1</td>");
        w.write(" <td>userA</td>");
        w.write(" <td>10</td>");
        w.write(" </tr>");
        */
        for (Member member : members) {
            w.write("   <tr>");
            w.write("       <td>" + member.getId() + "</td>");
            w.write("       <td>" + member.getUsername() + "</td>");
            w.write("       <td>" + member.getAge() + "</td>");
            w.write("   </tr>");
        }
        w.write("   </tbody>");
        w.write("</table>");
        w.write("</body>");
        w.write("</html>");
    }
}

서블릿을 사용하면 반복문으로 html을 작성해서 생성해낼 수 있다.

위에서 볼 수 있듯이 자바 코드만으로 html을 작성하는 것은 매우 귀찮다. 이렇게 끝까지 다 만들지 않고, 이미 있던 html에 수정할 부분만 고치며 동적으로 만들 수 있도록 도와주는 것이 템플릿 엔진이다. 그 중에는 JSP, Thymleaf 등이 있는데 다음으로는 JSP를 해볼 것이다.

반응형