[TIL] 아웃소싱(배달앱) 개발 전, AOP에 대하여(with. Jwt token)

J쭈디·2025년 1월 9일
0

Sparta_프로젝트

목록 보기
6/35

어제부터 2번째 팀 프로젝트가 시작되었다. 저번 과제에서 나는 필수구현 과제만 겨우 성공하고 도전 과제를 잃었다. (눈물)

그래서 이번 팀 프로젝트에서는 그 때 못 했던 도전 과제들을 놓치고 갈 수 없었고, 격차가 벌어지는 걸 방지하기 위해 조금 더 많이 공부하고 조금 더 많이 알아보기로 했다.

사실 이전까지의 프로젝트에서는 나는 편한 것을 자꾸 찾으려 하고, 새로운 것을 알아가면서 푸는 것보다 그냥 이미 아는 거니까~ 그러고 넘긴 경우도 꽤 있었던 거 같다.
그리고 그러다가 튜터님께 혼났다.

"아는 것만 하시면 안됩니다. 개발자가 되고 싶으시다면 새로운 것을 계속 공부하세요."

그러한 일침을 듣고, 모르는 부분을 독파하기로 결정했다. 나는 jwt 토큰을 제대로 이해하지 못하고 있었다.

0. 시작 전, JWT를 공부하다

1. jwt가 뭐죠?

일단 나는 인증 인가 관련 정확한 지식을 가지고 있는 지 질문 받았다.
그리고 답변을 이야기한 결과, 지식이 정확하지 않고 반만 맞는 거 같다며 코드를 조금 더 꼼꼼히 보는 노력을 하라는 이야기를 들었다.
jwt란 Json Web Token의 약어로, 서버와 클라이언트 간의 인증을 위한 암호화된 json 형태의 토큰을 말한다.

굳이 왜 jwt를 쓰는가?

  • 세션은 서버가 세션Id 값을 저장해서 사용자가 로그인 할 때마다 세션Id로 사용자를 인식한다. 그러나 이는 서버가 2개 이상일 때, 즉 인스턴스가 늘어날 때마다 새로운 세션 Id를 사용하므로 세션Id 값만을 저장하는 저장소가 필요해진다.
  • 이로 인한 낭비를 방지하기 위해 손쉽게 쓸 수 있는 것이 jwt이다.
  • 세션으로도 저런 문제들을 해결하고 사용하는 경우도 있다고 한다. 그런데 지금은 jwt를 공부하기 위한 질문이었던 만큼 그 부분은 넘어가고 설명해주셨다.

2. jwt Filter 속의 init, doFilter,destroy는 무슨 차이인가요?

나는 이번에는 jwt Filter에 대한 궁금증을 물어보았다.
init은 초기화, doFilter는 필터동작, destroy는 파괴라는 의미라고 한다.
제대로 공부 더 해오고 질문하라고요!!! 같은 튜터님의 내면의 소리를 들은 것 같다

아무튼, 그리하여 튜터님이 내 주신 숙제가 있었다. 어제 그거 보면서 정말 헷갈려서 나는 뭐지 아메바인가 이런 생각을 문득 했다.

 /*
  JwtFilter의 역할
  - 유저가 로그인 되어있는지 확인 (인증)
    */
  @Override
  public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
      throws IOException, ServletException {
    HttpServletRequest httpRequest = (HttpServletRequest) request;
    HttpServletResponse httpResponse = (HttpServletResponse) response;

    // 요청URL 중, 포트번호와 쿼리 사이의 부분을 가져와서 문자열형 url로 저장 (컨텍스트 경로 + 서블릿 경로)
    String url = httpRequest.getRequestURI();

    // startsWith는 특정 문자열로 시작되는지 체크하는 것이므로 user변수가 /user로 시작하는 문자열이라면 chain.doFilter(request,
    // response)를 사용하여 현재 처리 필터를 마치고 요청을 다음필터로 넘긴다. 이후 return하여 종료
    if (url.startsWith("/user")) {
      chain.doFilter(request, response);
      return;
    }

    // httpRequest.getHeader는 클라이언트 측 정보습득을 위해 사용, Authorization는 HttpServlet의 헤더 중, Jwt 토큰을 가져온다.
    String bearerJwt = httpRequest.getHeader("Authorization");

    // 가져온 토큰 값이 존재하지 않으면 httpResponse.sendError를 통하여 서블릿으로 예외처리한다.(상태코드, 메시지)
    // jwt 토큰은 Bearer jwt 형식으로 되어있기 때문에 String은 반드시 Bearer로 시작해야한다. jwtUtil.substringToken에서 이 건의 예외처리가 되어있음을 확인
    if (bearerJwt == null) {
      httpResponse.sendError(HttpServletResponse.SC_BAD_REQUEST, "JWT 토큰이 필요합니다.");
      return;
    }

    try {
      /*
      jwtUtil.substringToken()는 jwt 토큰값에서 앞에 붙은 Bearer을 제거하고 뒤의 토큰 값만 저장되도록 변환해주고, 그것을
      jwtUtil.extractClaims()를 이용하여 Claims 값만 추출한 뒤에 claims에 저장한다. 만약 claims가 비어있다면 sendError를
      통하여 서블릿으로 예외처리 후 종료한다.
       */
      Claims claims = jwtUtil.extractClaims(jwtUtil.substringToken(bearerJwt));
      if (claims == null) {
        httpResponse.sendError(HttpServletResponse.SC_BAD_REQUEST, "잘못된 JWT 토큰입니다.");
        return;
      }

      /*
      setAttribute("키값 문자열",저장객체)를 이용하여 httpRequest에 값을 저장한다.
      userId 키값을 가진 claims의 sub값을 추출해 Long형으로 변환하여 저장
      email 키값의 email값을 저장
      userRole 키값을 가진 userRole값 저장
      -> 이 부분은 책임분리가 안 되어 있음.
      */
      httpRequest.setAttribute("userId", Long.parseLong(claims.getSubject()));
      httpRequest.setAttribute("email", claims.get("email"));
      httpRequest.setAttribute("userRole", claims.get("userRole"));
      
      //뒷 부분은 catch  부분 예외처리라 생략

아무튼 이러한 형태로 열심히 공부해서 가져갔다.
그리하여 오늘은 드디어 jwt 이해했으니까 로그인 로그아웃 회원가입 해야지!!! 이러고 방방 뛰면서 코드를 열었다.

그런데 맞다... 우리 다 같이 정해서 AOP 적용하기로 했지..

1. AOP 적용을 하여 auth 부분을 만들자.

1. 그래서 AOP가 뭔데요?

  • aop는 Aspect Oriented Programming의 약어라고 한다.
  • 찾아보니 트랜잭션이나 로깅 등 부가기능을 모듈화해서 생산성을 향상시키는 것이라고 한다. 쎄함을 감지하였다.
  • 저는 이거 회원가입 로그인 로그아웃만 해도 되는 거에요? 다른건요? 하고 두리번 거리니 팀장님 왈 "aop적용하셔야 해서 그거도 좀 걸릴거에요~" 2차 쎄함 감지
  • 튜터님 왈 "aop 적용은 굳이 안 하시고 하셔도 됩니다." 3차 쎄함 감지

그러나, 뭔가 못 하겠다고 하면 배제시키고 할 수도 있었을 거 같은데 내가 그러기가 싫었다. 이번에야 말로 공부에 혼을 갈아 넣어라

아무튼 그리하여 aop에 대해서 조금 더 알아보기 위해 다른 반 세션을 들어보았다.

AOP의 장점, 쓰는 이유

  • 쉽게 가독성을 올려주기 때문에 결과적으로 코드 생산성을 향상시켜준다.

2. 이 코드를 수정하여 실행하라

이번에는 코드 수정 전, 초기 코드로 받은 코드에서 에러가 났다. 이전에 문제가 되었던 부분이 그대로 에러가 된 것 같다.

1. 에러부터 잡으세요 !

사실 저번 과제가 와장창이었던 이유는 나는 도전과제 실행이 계속 안 된다는 이유로 아래의 에러를 포기하고 그냥 내 추측대로 코드를 고쳐서 제출했기 때문이다.
대학교 때 생각하면 말도 안되는 일인데 나 왜 그랬지 하는 생각이 문득 들었다.

그리하여 열심히 눈치 보면서 에러에 대해 질문했다.


이렇게 긴긴 에러가 아래까지 쭉 내려오는데, 알고보면 별 거 없어서 허탈하다.
코드들이 이렇게 쭉 나올 때는 개인적으로 보는 2가지 팁이 있다.
1. 파란색글자.java를 주의 깊게 보기
2. 오른쪽으로 쭉 당겨서 길게 삐져나온 코드가 있다면 모두 읽어보기

아니나 다를까 이 문제에도 있었는데, 이렇게 되어 있었다.

그리고 계속 보니 아래에 이런 것도 있었다.

둘 다 동일한 의미로 보이는데 찾아보니 의존성 주입에서 문제가 발생한 것이었다. 의존성 주입 중에 jwtUtil 클래스의 의존성 주입이 문제가 발생한 것이고, 해당 클래스에 가 본 결과 jwt 토큰을 사용하기 위한 암호키(대칭키)가 제대로 작성되지 않았던 것이다.

그래서 어떡하지 하고 머리 굴리다가 튜터님이 암호키를 만들어 주셨다.
사실 보호과 나와놓고 암호키도 못 만들리 없는데.... 스스로 부끄러워졌다.
다음에는 암호키 만드는 것도 다시 복습을 해야할 것 같다.

이 코드는 과제로 받은 샘플코드 기반으로 만들어졌는데 어차피 각자의 로컬에서 테스트를 할 예정이라 몇몇 파일들이 ignore 처리 되어있었다.

그 중 하나가 application.properties 파일이었다. 이 파일은 애초에 샘플에서 빼고 주셨다. 그리고 데이터 베이스 의존성도 각자 마음대로 추가하라고 하신 우리 팀장님.... git ignore 파일 탐나서 다음에 활용 좀 해도 될까요? 하고 끝날 때 쯤 물어볼 생각이다. 후후후

2. 회원가입을 만들어라!

회원가입을 만들고 있다. 그런데 왜 이렇게 어렵지? aop를 적용하라고요?
아무리 찾아봐도 aop는 분리, 모듈화 이런 키워드로 이어지고 있었다. 근데 이게 과연 구현하면서 가능한 점인가? 라는 의문이 들어서 일단 구현을 먼저 해도 될까요? 라고 다른 분에게 문의를 했다.
다른 분이 aop는 리팩토링하면서 적용하라고 하셨다.

난 왜 고민한거지?

아무튼 그리하여 조금씩 코드를 쓰고 있는데 여기서 또 하나 문제, 나는 코드를 짜는 게 내 방식이 고정되어 있는 편인 거 같다. 근데, 단체 코드이다 보니 뭘 해야할지 모르겠다. 다른 분들 코드를 봐도 뭘 참고해야할지도 모르겠어서 일단 댕무시하고 그냥 내 스타일대로 가보기로 했다.

회원가입부터 만들어주고~ 테스트 가자!!

롸? 뭐지? 왜지?
라는 생각에 1차 멘붕, 심지어 터미널 쪽에서 8080 어쩌구 웅엥 하는 거 봐선 있으면 안되는 포트 에러가 난 건가?? 싶어서 찾아봤더니...

허탈하게도 내가 여러 코드를 한 번에 열어봤는데 그 중에 하나를 데베 연결해둔 채 방치했던 거였다.
그렇지, 데베가 같은 포트로 두개가 동시에는 있을 수 없지요.
그래서 해결했는데 또 8080 에러메시지 외에 동일증상이 나타나길래 저 jwt 토큰에 대한 예외문구를 찾아봤다.

결론적으로 나는 멍청이라는 걸 느끼며 어제 열심히 주석으로 달고 공부했던 부분...의 user를 auth로 바꾸었더니... 허허... 되더랍니다.
으아아아아아악!!화나..!!!!

그리하여 이렇게 평화로운 회원가입 성공...;;

어....이거..1월 8일에 작성된 건데....출간을 누른 줄 알았는데 임시저장....이었네요.....

없어져서 식겁했는데 다행히 임시저장에 있더라고요. 근데 왜 12월 것들도 있는걸까요..?

profile
언제 어느 위치에 있더라도 그 자리의 최선을 다 하는 사람이 되고 싶습니다.

0개의 댓글