3장_ 메모: HTTP 요청 라인 파싱과 멀티 스레드 처리

2026. 1. 31. 16:02·Book/자바 웹 프로그래밍 Next Step

 

웹서버를 띄우고 브라우저에서 http://localhost:8080 같은 주소로 접속하면 콘솔에 로그가 여러 줄 쏟아진다. 보통 처음엔 “내가 요청 하나 보냈는데 왜 두세 개가 오지?”가 제일 헷렸다.

결론부터 말하면:

  • 브라우저는 페이지 하나를 보여주기 위해 요청을 여러 개 보낸다.
  • 그리고 서버는 요청(정확히는 연결/소켓) 하나당 스레드를 하나 만들어 처리하는 구조로 짜두면, 로그에 Thread-0, Thread-1 같은 게 동시에 찍힌다.

 

“요청이 여러 개”라는 말은 무슨 뜻인가

브라우저 주소창에 /로 접속한다고 해서 서버가 /만 요청 받는 게 아니다.

브라우저는 보통 이런 걸 추가로 자동 요청한다.

  • / (메인 HTML)
  • /favicon.ico (탭 아이콘)
  • /css/... (CSS 파일이 있으면)
  • /js/... (JS 번들이 있으면)
  • /images/... (이미지 리소스가 있으면)

즉 “사용자 1명 = 요청 1개”가 아니라
“사용자 1명 = 여러 리소스를 가져오기 위한 여러 요청”이 기본이다.

그래서 서버 로그에는 짧은 시간 안에 요청이 여러 번 찍히는 게 정상이다.

 

포트가 매번 다른 이유: 클라이언트는 “임시 포트(ephemeral port)”를 쓴다

로그에 이런 식으로 찍힌다.

  • Connected IP: ..., Port: 56299
  • Connected IP: ..., Port: 56300

여기서 중요한 포인트는:

  • 8080은 서버 포트이다. (서버가 “기다리는” 포트)
  • 56299, 56300은 클라이언트(브라우저) 포트이다. (브라우저가 “요청 보낼 때 임시로 쓰는 포트”)

브라우저는 서버로 연결을 만들 때마다(또는 TCP 커넥션이 새로 생길 때마다)
OS가 임의의 포트를 하나 잡아준다. 이게 클라이언트 임시 포트이다.

그래서 요청이 2개면 임시 포트도 2개가 될 수 있고,
결과적으로 서버 로그에 “서로 다른 포트로 연결됨”이 보인다.

 

 

왜 Thread-0, Thread-1이 동시에 찍히나: 요청당 스레드 처리 모델

내가 만든 서버가 대체로 이런 흐름이라면:

  1. ServerSocket.accept()로 클라이언트 연결을 받는다
  2. 연결이 들어올 때마다 RequestHandler를 만들고
  3. new RequestHandler(socket).start() 로 스레드를 시작한다

이 구조에서는 연결 하나당 스레드 하나가 생긴다.

그래서 브라우저가 거의 동시에 요청을 2개 보내면

  • 첫 번째 요청은 Thread-0
  • 두 번째 요청은 Thread-1

이렇게 서로 다른 스레드가 동시에 실행된다.

여기서 “동시에”의 의미는 진짜 물리적으로 완전 동시에일 수도 있고(멀티코어),
아니면 OS 스케줄러가 빠르게 번갈아 실행하면서 동시에처럼 보이게 할 수도 있다.
중요한 건 서버가 요청을 한 줄로 줄 세워서 순서대로만 처리하는 구조가 아니라
각 요청을 독립적인 실행 흐름(스레드)으로 처리한다는 점이다.

 

 

실제로 서버가 읽는 HTTP 요청은 “텍스트”이고, 첫 줄이 핵심

서버는 브라우저가 보내는 요청을 소켓 InputStream으로 읽는다.
이 요청은 그냥 문자열(텍스트) 덩어리이다.

HTTP 요청은 크게 이런 구조이다.

  1. 요청 시작줄(Request Line)
  2. 헤더들(Header Lines)
  3. 빈 줄(Blank Line) ← 헤더 끝 표시
  4. (POST라면) 바디(Body)

즉, “첫 줄이 뭐냐”가 가장 핵심이다.

 

 

요청 시작줄(Request Line)의 형태: 요청마다 다를 수 있다

요청의 첫 줄은 보통 이런 형태이다.

<METHOD> <PATH>?<QUERY> HTTP/1.1

예시:

  • 메인 페이지 요청:
GET / HTTP/1.1
  • 파비콘 요청:
GET /favicon.ico HTTP/1.1
  • 회원가입 요청(쿼리스트링 포함):
GET /user/create?userId=javajigi&password=password&name=JaeSung&email=javajigi%40slipp.net HTTP/1.1

여기서 서버 구현이 보통 하는 일은:

  • 첫 줄에서 URL만 뽑는다. (/, /favicon.ico, /user/create?...)
  • URL에 ?가 있으면 경로와 쿼리를 분리한다.
  • 쿼리를 파싱해서 Map으로 만들고, User 같은 객체를 만든다.

즉 요청마다 첫 줄이 다르다는 건 너무 정상이다.
브라우저가 요청하는 리소스가 다르니까.

 

 

“각 요청의 마지막은 빈 문자열”이 무슨 뜻인가

서버에서 BufferedReader.readLine()으로 요청을 읽을 때, 많은 구현이 이런 루프를 쓴다.

  • 한 줄씩 읽는다
  • 빈 줄이 나오면 헤더가 끝났다고 판단하고 종료한다

HTTP 규칙에서:

  • 헤더의 끝은 CRLF로만 이루어진 한 줄, 즉 빈 줄이다.
  • 그래서 헤더를 다 읽으면 마지막에 빈 줄이 반드시 하나 나온다.

즉 “요청 마지막이 빈 문자열”이라는 말은 정확히는:

  • 헤더 블록의 끝이 빈 줄로 표시된다는 의미이다.

(그리고 이 뒤에 POST면 Body가 이어질 수 있다)

 

 

로그를 “서버 켰을 때 상황”으로 더 이해하기 쉽게 다시 구성해보기

실제로 서버를 켜고 브라우저로 접속하면 이런 흐름이 자주 나온다(예시 로그).

[INFO ] Web Application Server started 8080 port.

[DEBUG] Thread-0 New Client Connect! ... Port: 56299
[DEBUG] Thread-0 method=GET, path=/
[DEBUG] Thread-0 응답: webapp/index.html 내려줌

[DEBUG] Thread-1 New Client Connect! ... Port: 56300
[DEBUG] Thread-1 method=GET, path=/favicon.ico
[DEBUG] Thread-1 응답: webapp/favicon.ico 내려줌 (없으면 404가 맞음)

여기서 핵심만 뽑으면:

  • 브라우저가 / 요청과 /favicon.ico 요청을 거의 동시에 보냈다.
  • 요청이 2개라서 연결이 2개 잡혔다.
  • 연결마다 스레드가 따로 생겨서 Thread-0, Thread-1이 동시에 처리했다.
  • 각 요청의 첫 줄은 GET / ..., GET /favicon.ico ...처럼 서로 달랐다.
  • 요청은 헤더 끝에 빈 줄이 하나 존재한다.

 

 

 

참고: 로그에 Is a directory가 뜨는 경우의 전형적인 의미

이건 실제로 많이 겪는 케이스이다.

  • 요청 path가 /인데,
  • 파일로 읽으려면 보통 webapp/ 아래에서 /에 해당하는 파일을 찾아야 한다.
  • 그런데 그대로 webapp/ 디렉터리를 readAllBytes 하려고 하면 “Is a directory”가 터진다.

즉 / 요청을 받았으면 서버는 보통 이렇게 매핑한다.

  • / → /index.html

이걸 구현해주면 에러가 사라지고 브라우저도 정상 렌더링 된다.

 

 

 

 


출처 : 《자바 웹 프로그래밍 Next Step》, 박재성, 로드북

'Book > 자바 웹 프로그래밍 Next Step' 카테고리의 다른 글

3장_ 요구사항3: POST방식으로 회원가입  (0) 2026.02.01
3장_ 메모: 요청 메시지의 형태  (0) 2026.01.31
3장_ 요구사항2 : GET 방식으로 회원가입하기  (0) 2026.01.31
3장_ 요구사항1: index.html 응답하기  (0) 2026.01.30
3장_ 코드 이해: RequestHandler 클래스  (0) 2026.01.29
'Book/자바 웹 프로그래밍 Next Step' 카테고리의 다른 글
  • 3장_ 요구사항3: POST방식으로 회원가입
  • 3장_ 메모: 요청 메시지의 형태
  • 3장_ 요구사항2 : GET 방식으로 회원가입하기
  • 3장_ 요구사항1: index.html 응답하기
sqaxe1
sqaxe1
woojoo-devlog 님의 블로그 입니다.
  • sqaxe1
    Woojoo's Devlog
    sqaxe1
  • 전체
    오늘
    어제
    • 분류 전체보기 (148)
      • Backend (9)
        • Servlet (7)
        • Spring (2)
      • Frontend (1)
      • CS (0)
      • Book (33)
        • 자바 웹 프로그래밍 Next Step (30)
        • 테스트 주도 개발: 고품질 쾌속개발을 위한 TDD.. (1)
        • 성공과 실패를 결정하는 1%의 네트워크 원리 (2)
      • Engineering (0)
        • Testing (0)
      • Infra (6)
        • AWS (6)
      • Java (4)
      • Network (1)
      • 김영한 (28)
        • 자바 입문 (8)
        • 실전 자바 - 기본편 (6)
        • 실전 자바 - 중급편 (10)
        • 실전 자바 - 고급편 (4)
      • Web (39)
        • Web Basics (39)
      • Project (24)
        • NeoSquare (0)
        • Memo Evolution (24)
      • 정보처리기사 (0)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    개발서적
    aws
    java
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.6
sqaxe1
3장_ 메모: HTTP 요청 라인 파싱과 멀티 스레드 처리
상단으로

티스토리툴바