Project/TomorrowLand

화면상에서 권한별 동적 렌더링에대한 고민

kkkkkdddddhhhhh 2024. 2. 14. 22:14

TomorrowLand 질문글 화면

 

사용자가 작성한 사용자 본인의 게시글 및 댓글 View에는 수정 ,삭제 버튼이 보여야한다.

현재 로그인해 접속해있는 사용자의 권한을 어떤방법으로 확인해 권한별로 동적 렌더링을 진행할지에대한 고민이 시작됬다.

(이전 프로젝트에서는 ThymeLeaf를 사용해 서버에서 html을 만든다음에 브라우저에게 내려줘, Spring security 및 권한 연동이 간편했지만, 이번 프로젝트는 React를 사용해 이러한 고민이 들게되었다...)

 

자 그럼 어떤 방식으로 브라우저에게 지금 로그인해 접속한 유저가 해당 게시글 , 댓글을 작성했다고 알려 줄 수 있을까? 

나는 2가지 방법을 고안했다.

1. BackEnd 에서 권한을 확인한 후 프론트엔드에게 동적 렌더링 여부를 알려준다.

2. FrontEnd 에서 최초 로그인시 응답받은 JWT Token에서 Payload를 가져와 웹 스토리지에 저장 후 이메일 일치 여부로 해당 사용자인지 확인한다.

 

위의 두 방법중 무엇이 더 옳은지 판단이 바로 들지않았다.

권한을 확인한다는점과 JWT Token을 다루는점에서 서버내의 별도 로직이 필요한거 같기도하고,

React를 통해 확인하는편이 간단해보이기도 했다.

 

그래서 위 두 방식을 모두 구현해봤다.

댓글에 관한 동적 렌더링은 서버에서 인증객체를 통해 확인하는 첫번째 방식을,

게시글에 관한 동적렌더링은 프론트에서 JWT Payload를 가져와 현재 접속한 사용자와 게시글의 아이디를 비교해 일치여부를 통해 확인하는 두번째 방식을..


 

1. BackEnd 에서 권한을 확인한 후 프론트엔드에게 동적 렌더링 여부를 알려준다.

어떤 방식으로 "해당 댓글은 수정과 삭제가 가능한 댓글이야" 라고 Fronend에게 알려 줄 수 있을까?

간단하게 응답 필드를 추가해 수정 삭제가 가능한 권한일 경우 True, 아니라면 False를 내려주면 된다.

 

modifiable이란 필드값을 통해 권한을 알려주자.

그럼 어떻게 해당 사용자의 댓글에만 modifiable을 true로 만들어 줄 수 있을까?

 

우선 댓글을 조회 할 때 요청에 accessToken을 담고,

accessToken을 통해 해당 사용자의 인증 객체가 생성된다면 -> Service에 getAllWithPrinciapl() 메서드를 사용하게된다.

인증객체가 생성되지않아 userPrincipal이 null일 경우엔 getAllNoPrincipal() 메서드를 통해 modifiable이 전부 false인 댓글을 응답받게 된다.

 

 

게시글 아이디를 통해 댓글들을 모두 가져온뒤에

그 댓글들 중 댓글 조회를 요청한 사용자의 username과 댓글 작성한 username이 일치한다면 modifiable을 true로 바꿔주는 로직을 추가했다.

 

 

React에서 댓글을 화면에 뿌려줄때 응답받은 modifiable이 true라면 삭제버튼을 보여주도록해 권한별 동적 렌더링에 성공했다.

 

 

2. FrontEnd 에서 최초 로그인시 응답받은 JWT Token에서 Payload를 가져와 웹 스토리지에 저장 후 이메일 일치 여부로 해당 사용자인지 확인한다.

이번엔 서버에서 별다른 로직을 거치지않고 React에서 Payload를 복호화해 userEmail을 세션 스토리지에 저장하고, 

세션 스토리지에 저장된 이메일과 작성된 게시글의 사용자 이메일이 일치한다면 수정 , 삭제 버튼이 보이게끔 로직을 만들었다.

 

우선 로그인시에

 

AccessToken은 Local Storage에 담고,

AccessToken을 복호화해 얻은 Subject에 userEmail은 Session Storage에 담았다.

(로컬 스토리지는 브라우저가 종료되도 남아있지만, 세션 스토리지의 경우 종료시 사라진다. 아무래도 한번 복호화를 한 정보기때문에 세션스토리지에 담기로 결정했다.)

 

 

위에서 설명한대로 현재 로그인해 접속한 사용자의 이메일(세션스토리지에 저장된 이메일) 과 게시물을 작성했던 작성자의 이메일이 일치한다면 수정, 삭제 버튼이 보이도록 렌더링을 했다.

 

이 과정에서 사용자의 이메일은 사용자 식별을위해 Unique하게 스키마를 변경했다.

(기본 회원가입 Rquest Validation을 통해 중복 이메일 예외를 만들긴했지만, DB까지 확실하게 하고싶었다.)

 

 


 

자, 그럼 두 가지 방식을 모두 구현해봤는데 무엇이 더 나은방법일까 생각해봤다. 

아무래도 사용자 권한에 관한 로직이다보니 Backend에서 처리하는것이 맞지않을까? 로 기울어지던차에 혼자 게시글을 올리고 답변을 달고 Self QA를 진행하다 첫번째 방식의 큰 결점을 발견했다.

 

AccessToken은 5분뒤 만료된다. 

AccessToken 만료시 RefreshToken을 통해 재발급받는 Reissue API를 요청해야하는데

 

Server에서는 AccessToken이 만료되더라도 로그인하지않는 사용자의 댓글 조회 요청으로 인식해 그냥 댓글을 뿌려준다.

쉽게말해 서버 입장에서 방금 온 요청이 AccessToken이 만료된 사용자의 요청인지, 그냥 비 로그인 사용자의 요청인지 구분할 수 가 없다.

따라서, 댓글을 작성하고 5분동안 따로 Reissue 요청이 없을 경우 내가 작성한 댓글임에도 불구하고 댓글 삭제 버튼이 보이지않는다.

 

내가 만든 서버 로직의 문제일수도있지만, 현재로서는 FrontEnd에서 Payload에서 사용자 이메일을 가져오는것이 맞다고 생각이들었다.

 

그렇다면 FrontEnd에서 Payload를 복호화해도 되는가?

  • 프론트엔드의 역할로 UI Rendering , UX 최적화 등이 있지만, Client 상에서의 간단한 보안처리도 포함된다 생각된다. → 로그인, 사용자의 권한 전역상태관리 등…
  • JWT accessToken에는 애초에 탈취의 위험등으로인해 민감하고 치명적인 사용자 정보를 포함고있지않다. Client에서 payload에 담긴 userEmail 정보를 저장해도 괜찮다 판단

또한 get 요청을 통한 DB 사용자정보 읽기 조회가 크게 비용을 먹지않는다고하지만,

FrontEnd에서 Payload를 복호화할 경우 서버의 DB 커넥션 비용이 일체 들지 않는다는 장점 또한 존재한다.

 

이러한 결론들로

화면단에서 JWT Payload를 가져와 권한별 동적 렌더링하도록 결정했다.