본문 바로가기

공부/TIL

23.11.24

뉴스피드 프로젝트 끝! 기본적인 기능은 지난 개인과제와 비슷했다. Spring Security를 이용하여 인증기능을 구현하고 뉴스피드를 작성하는 것. 지난 과제에서 실패했던 부분을 모두 내 손으로 직접 구현했다. 그래서 너무 만족스럽고 성취감이 느껴진다. 지난 과제의 패착과 이번 과제의 성취, 그리고 이번 과제의 부족한점에 대해 서술하겠다. 우선 이번 과제에 대한 소개는? 레파지토리의 READ ME를 읽으면 이해할 수 있도록 내가 또 문서작업을 해놨다. 

https://github.com/NBCamp-B09-Newsfeed/Backend

 

GitHub - NBCamp-B09-Newsfeed/Backend

Contribute to NBCamp-B09-Newsfeed/Backend development by creating an account on GitHub.

github.com

 

1. 지난 과제에서의 패착 (구현 순서의 실패로 흐름을 잃음)

지난 과제에서는 게시글 작성과 댓글 작성 기능까지 수행했었다. 하지만 가장 중요한 인증 관련을 구현하는데 실패했다. 실패의 원인은 어디서부터 손을 대야 할지 모르겠다는 것. 보안 인증의 흐름은 이해했지만, 어디서부터 구현을 시작해야 할지가 막막했다. 지금 돌이켜보니 처음은 JWT를 다루는 클래스부터 만들어야 했던 것 같다. 하지만 지난 과제에서는 FILTER단부터 구현을 시작했다. 아직 JWT를 다루는 클래스가 없는 상황에서 FILTER에서는 JWT를 다뤄야 하니 감이 안잡혔다.

이번에는 JWT부터 클래스 구현을 시작했다.

 

2. 팀프로젝트 구현 순서

우선 나는 CRUD를 담당했다. 인증기능 구현이 안된 상태였기에 인증기능이 필요하지 않은 단순 기능들을 얼른 구현했다. 3번의 프로젝트를 진행하면서 CRUD는 이제 손에 익었고, 패턴화가 되었기에 정말 빨리 끝냈다. 이제부터 승부였다. 지난과제에서 실패했던 인증기능을 CRUD에 엮어보자.

우리 팀원이 Spring Security를 이용한 인증 기능 구현에 어려움을 겪었다. 같이 문제를 해결하다가 서로 다시 공부해서 구현해오자! 약속을 하고 각자 구현 시작.

 

천천히 구현을 시작했다. JwtUtil을 만들어서 jwt토큰을 만들고~ 자르고~ 유효성을 검사하는 유틸 클래스를 구현했다. 그리고 FILTER를 구현했다. 필터의 역할은 요청헤더에서 JWT 토큰을 찾고, 유효성을 검사한다. 검사에 통과 되면? UserDetials에 유저 정보를 담아준다. 그리고 인증 값은 SecurityContextHolder에 담긴다. 이는 언제든 인증이 필요할 때마다 꺼내서 사용한다(jwt 토큰이 유효하다면). 그리고 WebSecurityConfig클래스를 구현했다. 이 클래스는 원하는 url요청은 인증을 하지 않고 통과시켜주겠다!를 구분하기 위해 사용했다. 우리 프로젝트에서는 로그인/회원가입페이지와 게시글 조회/검색기능은 인증하지 않기 위해서 인증을 허가해줬다. 아 여기서 또 Session 방식이 아닌 JWT를 이용한 방식을 하겠다고 선언해준다. Spring Security는 원래 Session방식으로 인증을 한다. 하지만 우리는 Jwt방식을 이용할 것이기 때문에 변경을 해준다. 이렇게 인증 관련 구현이 완성됐다.

이제는 Controller단에서 인증 관련 처리를 해주면 된다. 로그인시 jwt 토큰을 HttpResponse클래스를 통해서 header에 jwt토큰을 담아서 응답해주면 끝!

 

그후에 댓글 기능구현에 들어갔다. 새로운 Entity를 만들고, 사용자와 게시글과의 연관관계를 맺어줬다. 댓글은 게시글과 거의 같은 식으로 흘러간다. 글 게시시에 사용자 정보를 받아야 한다. 작성자가 누군지 알아야 하기 때문에. 그리고 수정/삭제시에는 작성자만 가능하도록 해야 한다. 이떄 위에서 사용한 인증 방식을 사용한다. 유저의 정보를 받을 때 

 

public ResponseEntity<CommonResponseDto> mypage(@AuthenticationPrincipal UserDetailsImpl userDetails)

@AuthenticaationPrincipal 어노테이션을 달고 아까 Filter에서 유저의 정보를 담은 UserDetials클래스에 유저 정보를 담아서 가져온다. 여기서 예외가 발생하지 않으면? 정상적인 인증이 완료 된 것.

 

마지막으로 예외처리를 해줬다. 회원가입시 정규 표현식을 지켰는지? 중복된 닉네임이 있는지? 게시글 수정/삭제 시 작성장인지? 인증이 필요한 요청은 인증이 유효한지? 등등 내가 처리할 수 있는 예외는 모두 응답할 수 있게 처리했다. 오류 상태 메세지를 응답한다. 성공하면 정보가 담긴 dto를 응답한다. 위의 깃 레파지토리에 API 명세서에 테스트 케이스들을 나눠서 저장 해두었다. 

이렇게 팀프로젝트를 무사히 마쳤다. 아니 아직 다음주 월요일에 발표를 해야 하지만.. 발표자는 나! 내가 프로젝트를 많이 준비 했으니 발표시에 막히는 부분은 없을 것 같지만 그래도 좀 더 들여다봐야겠다. 

문제점과 해결사항

1. 예외 발생시 클라이언트에게 어떤 예외가 발생했는지 알려주는 메세지와, 상태 코드를 담아서 반환 하고자 했습니다. 상태 메세지와, 코드를 담는 DTO를 CommonResponseDto를 이용해서 했습니다.

여기서 발생한 문제는 응답 body에 정보를 담지 않는 케이스(로그인)에서는 응답 바디에 성공/실패 여부와 상태 코드만 담아서 응답 하면 됐습니다.

하지만 응답 body에 정보를 담아서 응답 해야 할 케이스가 생겼습니다.

예를 들어 게시글 작성을 성공하면, 내가 작성한 게시글을 응답 body에 담아서 보내야 합니다. 이때는 CommonResponseDto를 사용할 수 없이, body에 정보를 넣어줄 다른 객체 예)MenuResponseDto를 이용해야 했습니다. 이렇게 코드가 구성이 되면, 실패 했을 때 상태메세지와 상태코드를 응답할 수 없었습니다.

 

[해결방법] 정보를 응답하는 모든 Dto에 CommonReponseDto를 상속 받게 했습니다. CommonResponseDto를 추상화하여 모든 Controller의 응답을 ResponseEntity로 받게 했습니다. 이렇게 코드 변경을 하니 성공햇을 때는 정보를 보내는 자식 클래스를 보내고, 실패를 했을 때는 CommonResponseDto를 응답해 실패 메세지와 코드를 반환했습니다.

 

[부족한 점] 성공시에도 상태메세지와 코드를 보낼 수 있으면 좋겠다고 생각했습니다. 상태코드는 ResponseEntity를 통해 할 수 있었지만, 성공 메세지를 보내는 부분이 어려웠습니다.

 

 

2. DTO 사용에 어려움이 있었습니다. 요청마다 필요한 정보가 달라서 요청에 맞는 DTO를 만들어줬습니다. 이렇게 생긴 DTO들이 나중에는 너무 많이 생겨서 관리에 어려움이 있었습니다.

예를 들어 로그인 요청 DTO와 비밀번호 확인 DTO, 회원수정 요청 DTO가 다 따로 있어야 했습니다. 작은 프로젝트에도 DTO가 너무 많이 늘어났다고 생각했습니다.

'공부 > TIL' 카테고리의 다른 글

23.11.28  (0) 2023.11.28
23.11.27  (1) 2023.11.27
23.11.23  (1) 2023.11.23
23.11.22  (0) 2023.11.22
23.11.21  (1) 2023.11.21