본문 바로가기
이것저것 공부

내가 보려고 만든 게시글(JWT 로그인버전 소스코드 분석)

by 성동구불주먹 2025. 4. 24.

 

로그인 정보를 클라이언트에서 받아와 처리하는 소스코드다.

실무에서 자주 쓰지 않았기 때문에, 복기할 겸 정리함

 

AccountController.java

    @PostMapping("/api/account/login")
    public ResponseEntity Login(@RequestBody Map<String, String> params, HttpServletResponse res) {
        Member member = memberRepository.findByEmailAndPassword(params.get("email"), params.get("password"));

        if (member != null) {
            JwtService jwtService = new JwtServiceImpl();
            int id = member.getId();
            String token = jwtService.getToken("id", id);

            Cookie cookie = new Cookie("token", token);
            cookie.setHttpOnly(true); // front에서 접근 못함
            cookie.setPath("/"); // 모든 경로에 유효함

            res.addCookie(cookie);

//            return ResponseEntity.ok().build();
            return new ResponseEntity<>(id, HttpStatus.OK);
        }

        throw new ResponseStatusException(HttpStatus.NOT_FOUND);
    }

 

@PostMapping : 클라이언트에서 요청한 Post방식의 url과 동일하게 설정

 

public : 접근 제어자로, Spring에서는 @Controller나 @RestController에 선언된 메서드들은 외부 요청이 들어오기 때문에 public으로 선언해야만 작동한다.(해당 controller는 @RestController임)

 

ResponseEntity : 서버에서 클라이언트로 보내는 응답을 커스터마이징 가능 / return 값에 선언돼있는 것처럼 상태 코드 같은 상세내용 설정 가능(본문, 상태 코드, 헤더 설정 등)

 

@RequestBody : HTTP 요청 본문(body)에 포함된 데이터를 자가 객체로 변환해주는 역할이다.

클라이언트가 서버로 데이터를 보낼 때, JSON,XML,텍스트 등 어떠한 형태로 보내도 서버에서 자바 객체로 변환해 줌

주로 POST나 PUT 요청에서 사용

 @PostMapping("/user")
    public String createUser(@RequestBody User user) {
        // 클라이언트에서 보낸 JSON 데이터를 User 객체로 변환
        return "Received user: " + user.getName() + ", Age: " + user.getAge();
    }

 

➕ 주로 Map 형식과 자주 사용되는 이유?

동적으로 변하는 키-값 쌍의 데이터를 처리하기 쉬워서다.

클라이언트에서 어떤 필드가 올지 모른다거나, 데이터 구조가 너무 복잡하다던지 필드가 여러 개여도 매번 새로운 클래스 생성 없이 Map으로 처리 가능

 

HttpServletResponse : 서버에서 클라이언트(브라우저 등)로 보내는 HTTP 응답을 조작할 수 있게 해주는 객체임 (주로 쿠키, 리다이렉트, 파일 다운로드 등 구체적 조작)

 

 


 

 

‼️ResponseEntity랑 HttpServletResponse 예제 비교

 

먼저 ResponseEntity

@PostMapping("/api/login")
public ResponseEntity<String> login() {
    return ResponseEntity
        .status(HttpStatus.OK)
        .header("Custom-Header", "value")
        .body("로그인 성공!");
}

 

상태 코드, 헤더, 본문까지 설정 가능 / JSON 반환이나 메세지 응답에 많이 사용

 


HttpServletResponse

@PostMapping("/api/login")
public ResponseEntity<Integer> login(HttpServletResponse res) {
    Cookie cookie = new Cookie("token", "abc.def.ghi");
    cookie.setHttpOnly(true);
    cookie.setPath("/");
    res.addCookie(cookie);

    return ResponseEntity.ok(1);
}

 

쿠키를 직접 조작해야 해서 res.addCookie() 사용

이런 작업은 ResponseEntity로는 못 함

 

 


 

🧐 왜 JWT + 쿠키를 같이 쓰는가? 세션(sessionStorage)도 있는데?

 

JWT만 사용하게 됐을 때,

- 로그인 시 서버가 JWT(서명된 토큰)를 생성해서 클라이언트에 전달함

- 클라이언트는 이후 요청마다 이 JWT를 Authorization 헤더에 실어 보냄

Authorization: Bearer <token>

 

근데, 이렇게 되면

클라이언트가 매번 직접 헤더에 넣어줘야 하기 때문에 프론트에서 번거롭고

XSS공격에 취약할 수 있다.(localStorage에 저장하면 js로 접근 가능)

 

그래서 쿠키에 JWT를 넣는 방법이 등장

- JWT를 HttpOnly 쿠키에 저장하면, 자바스크립트에서 접근할 수 없어서 보안강화

- 브라우저가 요청할 때마다 자동으로 쿠키 전송 -> 개발자가 헤더 관리할 필요 X

Cookie cookie = new Cookie("token", jwtToken);
cookie.setHttpOnly(true);
res.addCookie(cookie);

 

 

그럼, Cookie 말고 sessionStorage/localStorage 써도 되는 거 아닌가?

- 일단, cookie와 다르게 보안기능 없음(HttpOnly 같은..) 그로 인해 XSS 공격에 취약!

- 탭, 창 닫으면 삭제돼버림(Cookie는 만료시점 설정가능)

- Cookie와 다르게 수동으로 전송 필요함(Cookie는 매 요청마다 서버로 자동 전송됨)

 

반응형