๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ

ํ”„๋กœ์ ํŠธ/Airbnb Clone

[ํŠธ๋Ÿฌ๋ธ” ์ŠˆํŒ…] ํ…Œ์ŠคํŠธ๋กœ ์•Œ์•„๋ณด๋Š” ์ปจํŠธ๋กค๋Ÿฌ์—์„œ HttpSession ์‚ฌ์šฉ ์‹œ ์ฃผ์˜์ 

Junit5 ๋ฐ Mock ์— ๋Œ€ํ•ด ์„ค๋ช…๋“œ๋ฆฌ์ง€ ์•Š๋Š” ์  ์–‘ํ•ด ๋ถ€ํƒ๋“œ๋ฆฝ๋‹ˆ๋‹ค.
ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ณด๋‹ค๋Š” ์ปจํŠธ๋กค๋Ÿฌ์—์„œ HttpSession ์„ ์‚ฌ์šฉํ•  ๋•Œ์˜ ์ฃผ์˜์ ์„ ์•Œ์•„๋ณด๋Š” ์‹œ๊ฐ„์„ ๊ฐ€์ ธ๊ฐ€์…จ์œผ๋ฉด ์ข‹๊ฒ ์Šต๋‹ˆ๋‹ค.

 

์ปจํŠธ๋กค๋Ÿฌ์—์„œ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ HttpSession ์„ ์ฃผ์ž…๋ฐ›์•„์„œ ์‚ฌ์šฉํ•  ๋•Œ ์ฃผ์˜ํ•  ์ ์ด ์žˆ์Šต๋‹ˆ๋‹ค.

 

์™œ ์ฃผ์˜ํ•ด์„œ ์‚ฌ์šฉํ•ด์•ผ ํ•˜๋Š”์ง€ HttpSession ๊ณผ HttpServletRequest.getSession() ์˜ ์ฐจ์ด๋ฅผ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋กœ ์•Œ์•„๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

 

โญ HttpSession ๋ฅผ ๋ฉ”์„œ๋“œ์˜ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ์ฆ‰์‹œ ์ฃผ์ž… ๋ฐ›๊ธฐ

  • ์ฝ”๋“œ ์„ค๋ช…

- ํšŒ์›์ด ์ธ์ฆ ์ฝ”๋“œ๋ฅผ ์ž˜๋ชป ์ž…๋ ฅํ•ด์„œ ์ด๋ฉ”์ผ ๊ฒ€์ฆ์— ์‹คํŒจํ•˜๋ฉด if ๋ฌธ์ด ๊ฑฐ์ง“์ด ๋ฉ๋‹ˆ๋‹ค.

- Session ์— ๊ฐ’์„ ์„ค์ •ํ•˜์ง€ ์•Š๊ณ , ๋ฉ”์„œ๋“œ๊ฐ€ ์ข…๋ฃŒ๋ฉ๋‹ˆ๋‹ค.

@GetMapping("/email/authenticate")
public ApiResponse<Void> authenticateEmail(@RequestBody @Valid EmailAuthenticationRequest request,
                                           HttpSession session) {
    if (mailService.isValidMail(request.toServiceRequest())) {
        session.setAttribute(MAIL_VERIFIED_MEMBER, true);
        return ApiResponse.ok();
    }
    return ApiResponse.status(HttpStatus.BAD_REQUEST);
}

 

  • ํ…Œ์ŠคํŠธ ์ฝ”๋“œ

์ด ๋ฉ”์„œ๋“œ๊ฐ€ if๋ฌธ์ด ๊ฑฐ์ง“์ด ๋  ๋•Œ, ์„ธ์…˜์ด ์ƒ์„ฑ์ด ๋˜์ง€ ์•Š๋Š”์ง€ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ์งœ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

@DisplayName("์ด๋ฉ”์ผ ์ธ์ฆ์ด ์‹คํŒจํ•˜๋ฉด HttpSession์€ ์ƒ์„ฑ๋˜์ง€ ์•Š๋Š”๋‹ค.")
@Test
void ํ…Œ์ŠคํŠธ() throws Exception {
    // given
    String email = "123@naver.com";
    String authenticationCode = "312njacz";
    
    EmailAuthenticationRequest request = new EmailAuthenticationRequest(email, authenticationCode);

    //==Mock ๊ฐ์ฒด๋ฅผ ์‚ฌ์šฉํ•ด์„œ ์ด๋ฉ”์ผ ๊ฒ€์ฆ์— ์‹คํŒจํ–ˆ์„ ๋•Œ๋ฅผ ๊ฐ€์ •==
    given(mailService.isValidMail(request.toServiceRequest()))
           .willReturn(false);

    // when then
    MvcResult mvcResult = mockMvc.perform(
                    get("/member/email/authenticate")
                            .contentType(MediaType.APPLICATION_JSON)
                            .content(objectMapper.writeValueAsString(request)))
            .andDo(print())  //==MockHttpServletRequest/Response ๋‚ด์šฉ์„ ์ฝ˜์†”์— ์ถœ๋ ฅ==
            .andReturn();

   //==getSession(false)๋ฅผ ํ•ด์•ผ ์„ธ์…˜์ด ์—†์„ ๋•Œ, MockHttpSession ์ด ๋งŒ๋“ค์–ด ์ง€์ง€ ์•Š์Œ==
   HttpSession session = mvcResult.getRequest().getSession(false);
   Object sessionValue = mvcResult.getRequest().getSession(false).getAttribute(MAIL_VERIFIED_MEMBER);

   assertThat(session).isNull();
   assertThat(sessionValue).isNull();
}

 

 

์›๋ž˜ ์˜๋„๋Œ€๋กœ๋ผ๋ฉด ํšŒ์›์€ ์ด๋ฉ”์ผ ๊ฒ€์ฆ์— ์‹คํŒจํ–ˆ๊ธฐ ๋•Œ๋ฌธ์— ์„œ๋ฒ„๋Š” ์„ธ์…˜์„ ์ƒ์„ฑํ•˜๋ฉด ์•ˆ๋ฉ๋‹ˆ๋‹ค.

๊ทธ๋ ‡๊ธฐ์— assertThat(session).isNull() ๊ณผ assertThat(sessionValue).isNull() ์˜ ๊ฒฐ๊ณผ๋Š” ์„ฑ๊ณตํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

ํ•˜์ง€๋งŒ, ํ…Œ์ŠคํŠธ ๊ฒฐ๊ณผ๋Š” ์•„๋ž˜ ์‚ฌ์ง„์ฒ˜๋Ÿผ ์‹คํŒจํ–ˆ์Šต๋‹ˆ๋‹ค.

  • session ๊ณผ sessionValue ์— ๊ฐ’์ด ์ƒ์„ฑ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

 

  • POSTMAN ์œผ๋กœ ํ™•์ธ์„ ํ•ด๋„ ์„ธ์…˜์ด ์ƒ์„ฑ๋จ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.

 

  • ์ธํ…”๋ฆฌ์ œ์ด์˜ http ์œผ๋กœ ํ™•์ธ์„ ํ•ด๋„ ์„ธ์…˜์ด ์ƒ์„ฑ๋จ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.

 

โญ์™œ HttpSession ์ด ์ƒ์„ฑ๋˜์–ด ์ฟ ํ‚ค๋ฅผ ๋ฐ˜ํ™˜ํ• ๊นŒ์š”?

HttpSession ์˜ ๊ณต์‹๋ฌธ์„œ์ค‘ ์ผ๋ถ€์ž…๋‹ˆ๋‹ค.

ํ•ด๋‹น ๋ฌธ์„œ์—์„œ๋Š” HttpSession ์€ ServletContext ์˜ ์Šค์ฝ”ํ”„์— ์˜ํ•ด ๊ด€๋ฆฌ๋˜์ง€, ์Šคํ”„๋งContext ์˜์—ญ์ด ์•„๋‹™๋‹ˆ๋‹ค.

๋‹จ์ง€ ์Šคํ”„๋ง์€ HttpSession ์„ ํ†ฐ์บฃ์˜ ServletContext ๋กœ๋ถ€ํ„ฐ ๊ฐ€์ ธ์˜ฌ ๋ฟ์ž…๋‹ˆ๋‹ค.

Session information is scoped only to the current web application (ServletContext), so information stored in one context will not be directly visible in another.

 

๊ทธ๋ž˜์„œ HttpSession ์„ ์ปจํŠธ๋กค๋Ÿฌ์˜ ํŒŒ๋ผ๋ฏธํ„ฐ์— ์‚ฌ์šฉํ•˜๊ฒŒ ๋œ๋‹ค๋ฉด ํ•ด๋‹น ๋ฉ”์„œ๋“œ๊ฐ€ ํ˜ธ์ถœ๋  ๋•Œ๋งˆ๋‹ค ์Šคํ”„๋ง์€ HttpSession ์„ ํ†ฐ์บฃ์œผ๋กœ๋ถ€ํ„ฐ ๊ฐ€์ ธ์™€ ์ฃผ์ž…ํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

 

โญ HttpServletRequest.getSession() ๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ

HttpSession ์˜ side-effect ์— ์•Œ์•„๋ดค์œผ๋‹ˆ HttpServletRequest.getSession() ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์ด๋ฉ”์ผ ๊ฒ€์ฆ์ด ์‹คํŒจํ–ˆ์„ ๋•Œ(if๋ฌธ์ด ๊ฑฐ์ง“์ผ ๋•Œ) ์„ธ์…˜์ด ๋งŒ๋“ค์–ด ์ง€์ง€ ์•Š๋Š”์ง€ ํ™•์ธํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

  • ์ฝ”๋“œ ์„ค๋ช…

- ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ ํšŒ์›์ด ์ธ์ฆ ์ฝ”๋“œ๋ฅผ ์ž˜๋ชป ์ž…๋ ฅํ•ด์„œ ์ด๋ฉ”์ผ ๊ฒ€์ฆ์— ์‹คํŒจํ•˜๋ฉด if ๋ฌธ์ด ๊ฑฐ์ง“์ด ๋ฉ๋‹ˆ๋‹ค.

- Session ์— ๊ฐ’์„ ์„ค์ •ํ•˜์ง€ ์•Š๊ณ , ๋ฉ”์„œ๋“œ๊ฐ€ ์ข…๋ฃŒ๋˜์•ผ ํ•ฉ๋‹ˆ๋‹ค.

@GetMapping("/member/email/authenticate")
public ApiResponse<Void> authenticateEmail(@RequestBody @Valid EmailAuthenticationRequest request,
                           ๋ฐ”๋€ ๋ถ€๋ถ„  ====> HttpServletRequest servletRequest) { <====
    if (mailService.isValidMail(request.toServiceRequest())) { 
        HttpSession session = servletRequest.getSession();
        session.setAttribute(MAIL_VERIFIED_MEMBER, true);
        return ApiResponse.ok();
    }
    return ApiResponse.status(HttpStatus.BAD_REQUEST);
}

 

  • ํ…Œ์ŠคํŠธ ์ฝ”๋“œ

ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๊ฐ€ ํ†ต๊ณผํ•˜๋ฉฐ ์„ธ์…˜์ด ๋งŒ๋“ค์–ด ์ง€์ง€์•Š๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.

 

โญ๊ฒฐ๋ก 

1. ์ปจํŠธ๋กค๋Ÿฌ ๋ฉ”์„œ๋“œ ํŒŒ๋ผ๋ฏธํ„ฐ์— HttpSession ์„ ๋ฐ”์ธ๋”ฉํ•œ๋‹ค๋ฉด ์Šคํ”„๋ง์€ ServletContext ์—์„œ ์„ธ์…˜์„ ๊ฐ€์ ธ์˜ค๋ฉฐ, ServletContext ์ž…์žฅ์—์„œ๋Š” ์Šคํ”„๋ง์—์„œ ์„ธ์…˜์„ ์‚ฌ์šฉํ•œ๋‹ค๊ณ  ๊ฐ€์ •ํ•˜๊ณ , "JSESSIONID" ๋ผ๋Š” ์ด๋ฆ„์˜ ์ฟ ํ‚ค๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.

 

2. ๊ทธ๋Ÿฌ๋‹ˆ, ์กฐ๊ฑด์— ๋”ฐ๋ผ HttpSession ์„ ์ƒ์„ฑํ•ด์•ผ ํ•œ๋‹ค๋ฉด HttpSession ๋Œ€์‹  HttpServletRequest ๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

 

3. HttpServletRequest ์„ ์‚ฌ์šฉํ•  ๋•Œ, ์Šคํ”„๋ง์ด ServletContext ์—์„œ HttpSession ์„ ๊ฐ€์ ธ์˜ค๋Š” ์‹œ์ ์€ .getSession() ์„ ํ˜ธ์ถœํ•  ๋•Œ์ž…๋‹ˆ๋‹ค.

 

4. ์ถ”๊ฐ€๋กœ ์„ธ์…˜์„ ์‚ฌ์šฉํ•  ๋•Œ๋Š” .getSession() vs .getSession(false) ์˜ ์ฐจ์ด๋„ ์•Œ์•„์•ผ ํ•ฉ๋‹ˆ๋‹ค.

.getSession() ์€ ์„ธ์…˜์ด ์žˆ์œผ๋ฉด ์„ธ์…˜์„ ๋ฐ˜ํ™˜ํ•˜๊ณ  ์„ธ์…˜์ด ์—†์œผ๋ฉด ์ƒˆ๋กœ์šด ์„ธ์…˜์„ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.

.getSession(false) ์€ ์„ธ์…˜์ด ์žˆ์œผ๋ฉด ์„ธ์…˜์„ ๋ฐ˜ํ™˜ํ•˜๊ณ  ์„ธ์…˜์ด ์—†์œผ๋ฉด null ์„ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

* Difference Between request.getSession() and request.getSession(true)