최근 스스로 토이 프로젝트를 하고, 스터디를 하면서 JPA로 개발을 할 일이 굉장히 많아졌었다. 프로젝트를 하면서 느낀 고민은 과연 내가 JPA를 제대로 알고 있는가?라는 생각이 들었다.
그러한 생각이 들었던 주제 중 하나는 PK에 관한 것이었다. DB 테이블과 매핑되는 엔티티를 작성함에 있어서 Auto Increament로 증가하는 값으로 설정된 계정 테이블의 식별자를 노출하면 안된다고 생각했었다.
JWT 토큰을 만들 때, 권한 값과 함께 PK가 아니라 email 값을 넣었었다. 그러면 DB 식별자를 노출하지 않아도 된다는 생각이 들었기 때문이다. 요청 바디나 쿼리 스트링에 임의로 변경해서 들어오면 문제가 생기지 않을까라 생각했었는데 최근 생각이 조금씩 바뀌기 시작했다.
우선, PK가 아니라 JWT 토큰을 만들 때 이메일을 노출하는 방식을 사용하면 생기는 첫 번째 문제점은 이메일이라는 정보를 노출한다는 점이다. JWT 토큰 자체는 base64로 인코딩되어 있기 때문에 누구나 해당 값을 확인할 수 있다. 누구나 세션 목적으로 준 JWT 토큰을 브라우저에서 탈취하기만 한다면 해당 정보를 볼 수 있다.
굳이 불필요하게 ID로 사용될 가능성이 있는 이메일 정보를 노출하게 된 것이고 그 이메일로 전송해서 방해도 할 수 있다. 그래서 이러한 측면에서 좋지 않은 것 같다는 생각이 들었다. 개발자 분들에게도 물어보니, 이메일, 전화번호나 주소 같은 개인정보를 당연히 보여주면 안되는거며 이메일을 토큰에 넣는 건 좋지 않다고 이야기를 해주셨다. 또한, 다른 분들이 무슨 값을 넣는가도 보았는데 계정 정보의 PK를 넣는 분이 많았던 것 같다.
대신 이것에 대한 방어로직을 확실하게 하라는 이야기를 들었었다. 과거에 코드 세션 당시에도 들었던 이야기이고 내가 구현하고 있는 동아리 관련 프로젝트에도 해당되는 이야기이다. API 호출에 있어서 권한에 관해서 체크를 잘해야 한다는 이야기였다. 특히나 방어 로직이 제대로 되어있지 않아 동아리 관리자가 아닌데, 동아리원을 제명시켜버리고 동아리를 삭제해버리는 그런 문제가 발생하지 않게 해야한다는 것이었다.
그래서 그 말을 듣고 동아리를 구현함에 있어 이런 방어 로직을 구현하려고 노력했던 것 같다. 아래는 동아리 게시판 내에 게시글을 등록하려고 할 때의 코드이다.

해당 부분을 보면 getClubMemberWithvalidation이라는 부분이 있는데, 해당 부분에서는 동아리 내에 해당 게시글을 등록할 권한이 있음을 확인하는 로직이 들어있다. 이런 코드 외에도 동아리원을 방출하거나 하는 데에서도 동아리 내에 관리자 권한이 있는 지를 꼭 확인하는 코드가 필요함을 인지하고 코드를 작성해야겠다는 생각이 들었다.
또한 다른 분의 의견은 중요하지만 꼭 넣어야겠다면 암호화된 값으로 넣고 서버에서 다시 해석하는 방식으로 사용하라는 조언이 있었다. 이 과정은 AOP를 사용하라는 방식이었는데, 시간이 나면 AOP 연습도 할 겸 만들어봐야겠다는 생각이 들었다.
우선 JPA를 사용하다보면 굉장히 특정 객체를 조회해서 그 객체로 처리해야 하는 경우가 많다.
사실 이 부분이 내가 JPA를 잘 모르고 쓰나?라는 생각이 들었던 부분이다. 한 명은 여러 동아리를 가질 수 있고 동아리 별로 닉네임도 설정하고 권한을 설정하고 싶다고 생각을 하고 구현을 했었다. 이를 달성하려면 어쩔 수 없이 기존 회원 관리를 하는 테이블인 Member가 아니라 동아리 별 회원 정보를 기록하는 ClubMember라는 이름으로 동아리 별 회원 테이블을 하나 따로 만들어야 했다.
여기서 기본 JPA Repository를 쓰는 구현을 하다보니 문제가 발생했다. 아까의 Validation Check와도 관련이 있는데 우선 ClubMember 테이블을 봐보자.

코드 전체를 캡처하지는 못했지만 club과 member를 연관관계로 넣어놨다. 그런데 생각해보면 굳이 ClubMember를 조회할 때, 동아리랑 회원 정보는 필요가 없다는 생각이 들었다. 그리고 조회를 할 때 아래처럼 작성을 하게 됐었다.

이게 큰 문제인가? 싶긴 할 것이다. 사실 계정에 가질 이메일에 대해 Unique 제약조건도 이메일 중복 체크 및 방지를 위해 달아야 돼서 finByEmail에 email 컬럼에 대한 인덱스를 걸어야 하는 건 어쩔 수 없다.
하지만, 사실 어차피 회원 PK도 알고 동아리 PK도 아는데, 그럼 그냥 그거로 조회해오면 안되는 건가? 참고로 clubMember에는 동일한 회원이 동아리 중복 가입이 안되게 (동아리 FK, 회원 FK)형태로 Unique 인덱스를 걸어놨다. 그래서 딱 쿼리 한번만 날리면 되는데 저렇게 코드를 작성했어야 하나라는 생각도 들었다.
아무튼 이 부분에 대해서도 다른 개발자분들이 어떻게 하나 조사를 해봤었다. Foreign key 제약조건 자체를 안 걸어버리는 걸 보았고, 이 부분은 운영에 있어서도 굉장히 중요하다고 설명을 하는 걸 보았다. 특히나 이 부분에 있어서도 고민을 해야 할 게 Foreign key 제약조건을 걸기는 하지만 생명주기가 같은지? 꼭 로딩이 필요한 객체인지에 대해서도 고민을 해볼 필요가 있음을 느꼈다.
개인적으로 아래의 유튜브들을 보고 굉장히 많은 생각을 하게 된 것 같다. 특히 내가 무지성으로 ManyToOne, OneToMany를 걸어버리는 게 아닌가? 객체 간의 사이클에 관해서 고민을 너무 안해봤나? 하는 생각을 하게 되었고, 해당 프로젝트에 대해서 리팩토링을 하면서 조금 고민을 해봐야겠다.
※ 참고로 MySQL에서는 FK 제약조건을 걸면 해당 컬럼에 대해서 인덱스를 설정한다. 따라서, FK를 안 걸고 해당 키만 사용할 거면 인덱스를 걸어놔야된다. 안 그러면 조인할 때 레코드가 커지면 안 좋아진다. 그리고 그 이름이 마음에 안들면 직접 설정해줄 수도 있다.
참고자료: https://dev.mysql.com/doc/refman/8.4/en/create-table-foreign-keys.html
과거 했던 고민을 정리해봤는데, 프로젝트를 하면서 이 부분을 더 고민하고 새로운 프로젝트를 진행하고 리팩토링을 해봐야겠다.
또한, 첫번째는 개인 깃허브랑 카톡 내용이라 올리기 어렵지만 아래의 유튜브 내용이 좋다고 생각해서 링크를 남긴다!
참고 자료:
https://www.youtube.com/watch?v=6q0-IT5J0nI
https://www.youtube.com/watch?v=vgNHW_nb2mg&t=6s