본문 바로가기
교재 실습/자바 웹 프로그래밍 Next Step

4.2.1.3 요구사항 3 - POST 방식으로 회원가입 하기

by Jint 2025. 2. 20.
요구사항 http://localhost:8080/user/form.html 파일의 form 태그 method를 get에서 post로 수정한 후 회원가입 기능이 정상적으로 동작하도록 구현한다.

 

요구사항 2의 회원가입 문제를 GET 방식에서 POST 방식으로 데이터를 전달하려면 form.html의 form 태그 method 속성을 get에서 post로 수정하면 된다. method를 post로 수정하면 요청 라인은 다음과 같다.

 

POST /user/create HTTP/1.1

 

GET 방식으로 요청할 때 요청 URI에 포함되어 있던 쿼리 스트링이 없어지고 method가 GET에서 POST로 변경되었다. 요청 URI에 포함되어 있던 쿼리 스트링은 HTTP 요청의 본문(body)을 통해 전달된다. POST 방식으로 데이터를 전달하면서 헤더에 본문 데이터에 대한 길이가 Content-Length라는 필드 이름으로 전달된다.

요구사항 3에 대한 구현 과정은 다음과 같다. 먼저 헤더에 포함되어 있는 Content-Length의 값을 구해 본문의 길이를 구한다. 이렇게 구한 길이만큼 본문을 읽은 후 본문 데이터를 Map<String, Object> 형태로 변환하면 된다. 본문을 읽는 기능은 IOUtils.readData()로 구현되어 제공되고 있다. 이 과정에 따라 구현한 결과는 다음과 같다.

 

- RequestHandler.java

package webserver;

import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.File;
import java.nio.file.Files;
import java.net.Socket;

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

public class RequestHandler extends Thread {
    private static final Logger log = LoggerFactory.getLogger(RequestHandler.class);

    private Socket connection;

    public RequestHandler(Socket connectionSocket) {
        this.connection = connectionSocket;
    }

    public void run() {
        log.debug("New Client Connect! Connected IP : {}, Port : {}", connection.getInetAddress(), connection.getPort());

        try (InputStream in = connection.getInputStream(); OutputStream out = connection.getOutputStream()) {
            // TODO 사용자 요청에 대한 처리는 이 곳에 구현하면 된다.
            BufferedReader br = new BufferedReader(new InputStreamReader(in, "UTF-8"));
            String line = br.readLine();
            log.debug("request line : {}", line);

            if (line == null) {
                return;
            }

            String[] tokens = line.split(" ");
            int contentLength = 0;
            while (!line.equals("")) {
                line = br.readLine();
                log.debug("header : {}", line);
                if (line.contains("Content-Length")) {
                    contentLength = getContentLength(line);
                }
            }

            String url = tokens[1];
            // POST
            if ("/user/create".equals(url)) {
                String body = IOUtils.readData(br, contentLength);
                Map<String, Object> params = HttpRequestUtils.parseQueryString(body);
                User user = new User(params.get("userId"), params.get("password"), params.get("name"), params.get("email"));
                log.debug("User : {}", user);
            }
            /*
            // GET
            if (url.startsWith("/user/create")) {
                int index = url.indexOf("?");
                String queryString = url.substring(index + 1);
                Map<String, Object> params = HttpRequestUtils.parseQueryString(queryString);
                User user = new User(params.get("userId"), params.get("password"), params.get("name"), params.get("email"));
                log.debug("User : {}", user);
            } 
            */
            else {
                DataOutputStream dos = new DataOutputStream(out);
                // byte[] body = "Hello World".getBytes();
                byte[] body = Files.readAllBytes(new File("./webapp" + tokens[1]).toPath());
                response200Header(dos, body.length);
                responseBody(dos, body);
            }
        } catch (IOException e) {
            log.error(e.getMessage());
        }
    }

    private int getContentLength(String line) {
        String[] headerTokens = line.split(":");
        return Integer.parseInt(headerTokens[1].trim());
    }

    private void response200Header(DataOutputStream dos, int lengthOfBodyContent) {
        try {
            dos.writeBytes("HTTP/1.1 200 OK \r\n");
            dos.writeBytes("Content-Type: text/html;charset=utf-8\r\n");
            dos.writeBytes("Content-Length: " + lengthOfBodyContent + "\r\n");
            dos.writeBytes("\r\n");
        } catch (IOException e) {
            log.error(e.getMessage());
        }
    }

    private void responseBody(DataOutputStream dos, byte[] body) {
        try {
            dos.write(body, 0, body.length);
            dos.flush();
        } catch (IOException e) {
            log.error(e.getMessage());
        }
    }
}

 

이와 같이 HTTP는 사용자의 요청 형태에 따라 여러 개의 메서드(method)를 지원한다. 웹 애플리케이션을 개발할 때 가장 자주 사용하는 메서드는 GET과 POST 두 가지 이다. 하지만 HTTP 스펙은 GET, POST, 이외에 HEAD, PUT, DELETE, PATCH, TRACE, OPTIONS 메서드도 지원한다. 하지만 웹 애플리케이션 개발 초기에는 GET과 POST만 사용할 가능성이 높다. GET, POST만 사용하는 이유는 HTML에서 GET, POST 메서드만 사용 가능하도록 지원하고 있기 때문이다. 하지만 최근 경향은 REST API 설계와 AJAX 기반으로 웹 애플리케이션을 개발하는 방향으로 발전하고 있는데, 이 기반으로 개발할 때는 PUT, DELETE 메서드까지 활용할 것을 추천하고 있다.

HTML은 기본으로 GET과 POST 메서드만 지원한다. 앞의 콘솔 로그를 통해 확인해보면 알 수 있겠지만 HTML의 모든 <a> 태그 링크, CSS, 자바스크립트, 이미지 요청은 모두 GET 방식으로 요청을 보낸다. POST 방식은 <form> 태그를 통해 요청을 보낼 수 있는데 <form> 태그가 지원하는 method 속성은 GET과 POST 뿐이다.

나머지 메서드는 추후 서버와의 비동기 통신을 담당하는 AJAX에서 사용할 수 있다. AJAX는 8장에서 자세히 다룬다.

웹 애플리케이션을 개발할 때 GET과 POST를 사용하는 기준이 있어야 한다. 다음과 같은 기준으로 접근한다면 GET과 POST 중 어느 것을 사용할 것인지 판단하는데 참고가 될 수 있다.

GET은 서버에 존재하는 데이터(또는 자원)을 가져오는 것이고 POST는 서버에 요청을 보내 데이터 추가, 수정, 삭제와 같은 작업을 실행하도록 하는 것이다.

즉, GET은 서버에 존재하는 데이터를 조회하는 역할만 하는 것이지 데이터의 상태를 변경하지 않는다. 하지만 POST는 데이터의 상태를 변경하는 작업을 담당한다. GET과 POST만을 사용해야 하는 상황이라면 이와 같은 기준으로 구분하고 추후 PUT, DELETE와 같은 다른 메서드를 사용할 때 더 세분화해 사용할 수 있다.



참고도서 : https://roadbook.co.kr/169

 

[신간안내] 자바 웹 프로그래밍 Next Step

● 저자: 박재성 ● 페이지: 480 ● 판형: 사륙배변형(172*225) ● 도수: 1도 ● 정가: 30,000원 ● 발행일: 2016년 9월 19일 ● ISBN: 978-89-97924-24-0 93000 [강컴] [교보] [반디] [알라딘] [예스24] [인터파크] [샘

roadbook.co.kr

댓글