역시 뭐든지 쉽게쉽게 진행되는 것은 없는것 같다!
계획대로라면 MVP 진행상황대로 기본기능, 추가기능 까지 완료한 후 이번주 부터는 고급기능을 들어갈 차례였다.
하지만!! 우리의 코드상에 존재하는 여러가지 문제점이 또 발견되었다.
이번주 진행되었던 상황을 쭉 정리해볼 예정이었지만 다른사람들에게도 인사이트가 될 만한 소재가 생겨 소개를 해보려 한다.
개발을 진행하며 CustomException
을 도입해 예외가 발생했을 때 예외 이유를 구체적으로 사용자에게 알려주는 식으로 개발을 진행했다.
이렇게 개발했던 이유는 오류 메시지를 명확하게 보여줌으로써 어떤 오류인지 쉽게 파악하고 빠르게 해결하기 위함이었다.
그러나 이러한 방식은 보안 관점에서 다음과 같은 우려 사항이 있는것으로 판단되었다.
정보 노출
: 구체적인 오류 메시지는 공격자에게 시스템에 대한 중요한 정보를 제공할 수 있다. 예를 들어, "잘못된 비밀번호"라는 메시지는 공격자에게 해당 이메일 계정이 존재함을 알려줄 수 있다.브루트 포스 공격
: 비밀번호 오류 메시지를 통해 공격자는 계정 존재 여부를 확인한 후, 비밀번호를 추측하여 여러 번 시도할 수 있는 기회를 얻게 된다.피싱 공격
: 구체적인 오류 메시지를 통해 피싱 공격자가 실제 시스템에서 사용하는 메시지를 모방하여 더욱 신뢰성 있는 피싱 시도를 할 수 있다.시스템 취약점 노출
: 시스템의 내부 구조나 검증 로직에 대한 정보를 유추할 수 있어, 이를 악용한 공격이 가능해진다.
바로 팀원들끼리만의 약속된 에러코드 명세서를 작성하고 사용하는 것이다!!
1. 내부적으로 구체적인 에러 코드 사용
:
- 각 에러 상황에 대한 구체적인 에러 코드를 정의하고, 이를 명세서에 상세하게 작성한다.
- 예를 들어, 비밀번호 오류는 1004
, 사용자 ID 오류는 1003
과 같은 에러 코드를 사용한다.
- 이러한 명세서는 개발팀끼리만 공유하여, 내부적으로 문제를 파악하고 해결할 수 있도록 한다.
2. 백엔드와 프론트엔드의 협업
:
- 백엔드에서는 예외 발생 시 구체적인 메시지 대신 에러 코드를 프론트엔드로 전달한다.
- 프론트엔드는 이 에러 코드를 받아 사용자에게 적절한 일반 메시지를 파싱하여 보여준다.
따라서 다음과 같은 명세서를 작성해 보았다.
https://www.notion.so/ae994788803c48afabd8aad1e9027e60
에러 코드 명세를 정하고 커스텀 익셉션을 리팩터링 하면서 Response
로 코드번호만 나오도록 도입을 해봤다.
하지만 어떤 이유에서 였을까...
오류가 발생했을때 우리가 정한 CustomException
형태의 오류의 형태로 나오는 것이 아닌 Internal Server Error
가 나는 것을 확인했다.
디버깅을 돌려보니 컨트롤러 코드가 실행되지 않는것을 확인해 → 필터에서 문제가 생긴다는 것을 확인했다.
결국 2가지 문제를 해결하게 되었다.
문제1
- JWT Util 클래스 토큰에서 이메일 뽑는 메서드의 예외 처리가
CustomException
으로 처리되지 않았다.CustomException
은RuntimeException
을 상속받기 떄문에SignatureException
은 처리할 수가 없다!!
해결!!
- 따라서
CustomException
을 처리할 수 있는Exception
으로 처리하는 것으로 수정하고 로그도 추가하였다.
문제 2
아쉽게도 1번 문제를 해결한 후에도 오류가 해결되지 않았다. 다시한번 디버깅을 돌려보니
- jwtUtil 클래스의
getEmailFromToken(token)
메서드가 여전히 동작하지 않는것을 확인했다.- 오류의 이유는 if이 사용되고 있어 예외를 처리할 수 없는 상태였다.
해결!
- 예외를 처리하기 위해 if문이 아닌 try catch문으로 변경하였다.
이렇게 하여 예외가 CustomException 으로 정상적으로 처리되는 것을 확인할 수 있었다.
getEmailFromToken(token);
메서드안에 CustomException 을 던지도록 이미 처리했다.예외는 Try-catch문으로 처리해야한다. 근데 어떻게 서비스 로직에서는 if 문으로 예외 처리를 하는걸까?
그것은 바로 JPA, 트랜잭션과 관련이 있다.
서비스 로직에서 우리는 JPA를 활용한다. 즉 @Transactional 안에서 데이터가 처리된다! 이것을 기억하고 있자.
@Transactional
어노테이션을 사용하면, 해당 메서드 내에서 발생하는 모든 데이터베이스 작업이 하나의 트랜잭션으로 관리된다.@Transactional
메서드 내에서 예외가 발생하면 기본적으로 트랜잭션이 롤백된다.@Transactional
어노테이션은 런타임 예외가 발생할 경우 자동으로 트랜잭션을 롤백한다.RuntimeException
의 하위 클래스인 것이다.CustomException
을 RuntimeException
을 상속받아 만들었다. 따라서 CustomException
이 발생하면 트랜잭션이 롤백된다.CustomException
을 던질 수 있다.CustomException
을 던진다.아주 간단하고 빠르게 정리하면
서비스 로직에서 우리는 JPA를 활용한다. 즉 @Transactional 안에서 데이터가 처리되는데
트랜잭셔널 안의 메서드에서 예외가 발생하면 롤백이 되고 → 런타임 예외가 발생하면 트랜잭션이 롤백된다.
근데 우리는CustomException
을 런타임 예외를 상속받아 만들었다!!
따라서 서비스 단에서는 if문으로 예외처리가 가능하다.
이 처럼 우리는 CustomException
시작하여 보안상의 문제를 고려하게되고
디버깅, 필터, try-catch문, if문, JPA와 트랜잭셔널 까지 고려하고 적용되는 상황이 나왔다.
개발을 하다보면 한가지로 시작하지만 점차 다른것이 연결되고 연결되는 느낌을 받을때가 있다.
그때마다 "아!" 하면서 이게 이거구나 하는 쾌감이 있다.
어떨때는 머리를 싸메고 몇일동안 고민을 해야 해결되는 부분도 있지만 이러한 배움에서 오는 쾌감과 깨달았을때의 도파민! 개발자로서 수명이 깎이는 느낌도 들지만 이러한 배움에서 오는 도파민이 우리를 성장시키는 것 같다!!!
나의 깨달음이 모두에게 인사이트가 되었으면 한다!! 오늘의 개발일기 끝!!!!!!!!
기술적인 고민이 묻어나는 아티클 감명깊게 봤습니다~!!
역시 우리팀의 부리더!!