Pointcut 을 선언하지 않고, 파라미터를 체크하지 않은 단순 security context holder 에서 authentication 객체를 가져와 체크를 하게되면, authenticationprincipal 객체가 null일때 처리를 못하고 null pointer exception 이 발생하게 되는 문제가 있었다.
@Before 로 args < 로 principla 객체를 가져와서 모든 메소드에 principal 객체를 (인증이 필요한 메소드) 매개변수로 넣어주고 체크를 해서 처리하도록 했다.
오늘의 배운점🤔
팀 프로젝트 설계를 끝냈다.
다행히 팀 프로젝트 난이도가 크게 높지 않아서 설계가 예상했던것보다 얼마 안걸렸다.
오늘 내가 놀랐던건, 라이브 코딩으로 다른거를 참고하지 않고 테스트 코드에 대해서 팀원들에게 설명을 해주는데 한순간의 막힘도 없이 쭉쭉 써내려가고 있었다 ㄷㄷㄷㄷㄷ
한두달전에 비하면 정말 폭풍성장했다는걸 오늘 느꼈다.
스프링 AOP 를 알게 되었다.
전에는 AOP 관련 강의를 들어도 이해가 전혀 가지 않았는데.. 오늘 드디어 감을 살짝 익혔다.
따라서 중복되는 코드들에 적용을 시켜보았다. 전체적으로 봤을때 인증 관련 검증과 (객체가 null인지) binding validation 에서 사용할 수 있을것 같아서 적용시켜보았다.
인증 관련은 before 로 메소드 호출 전에 확인을 했고, validation (binding result 타입을 이용한 에러 메세지 전송) 은 around 로 메소드 호출 도중에 체크를 하고, 문제가 없다면 계속 수행하도록 구현했다.
코드가 굉장히 짧아졌다.
인증 관련은 인터셉터를 이용해서 접근을 가로챌까 생각을 했지만, aop는 단순히 어노테이션 혹은 패키지명을 적어주고 정의만 해주면 클래스 두개로만 처리가 가능했지만, 인터셉터는 각 컨트롤러 별로 클래스를 따로 생성해 관리해야할것 같았다.
인터셉터로 게시판까지 처리를 하고, aop를 적용시켰는데 너무 비교가 되어서 aop를 남기고 인터셉터 관련 메소드와 클래스는 제거했다.
aop의 편의성을 지금 알게되었다.... 지금이라도 알게되서 너무 다행이고 앞으로 코드를 작성할때 얼마나 많은 도움을 받을 수 있고, 한걸음 더 나아가 고민거리를 던져줘서 재미까지 챙겨주는 aop.. 사랑해요
특히 api를 설계할때, 기존의 개인 프로젝트는 단순한 틀만 만들고 꾸준한 수정을 진행했던 반면, 팀으로 진행하니 미리 여러 api들을 설계 후 진행해 크게 어려울게 없을것 같았다.
테스트 코드 작성
테스트 코드 작성에 대해서 대해서 새로운걸 알게되었다.
하나의 메소드가 하나의 책임만을 갖고있어야 한다는것.
해당 부분을 인지하고 테스트 코드를 작성할때, 알아서 코드도 리팩토링을 하게 되더라.
테스트코드를 매번 밀린 숙제 하듯이 작성했었는데, TDD의 필요성을 점점 느끼고있다.
팀프로젝트를 계속 진행할 일이 생긴다면.. TDD 방식으로 개발을 하는게 맞을까... 라는 생각을 꾸준히 해왔는데, 맞다 라는 쪽으로 기울어지고 있는것 같다.
내가 짜놓은 코드의 문제점에 대해서 또 한번 깨닳았다.
하나의 책임만을 갖고있어야 하는데, 메소드가 여러개의 행동을 동시에 진행한다던지, 그러한 비즈니스 로직이 여러개 존재했다.
마찬가지로 , 스프링이 여러 기능을 제공해주는데 단순히 자주 쓰는 기능만 사용하고, 다른 기능들은 찾아서 활용해볼 노력조차 하지 않고있었다.
대표적으로 어드바이스, AOP, 인터셉터이다.
인터셉터 같은 경우에는 매번 인증을 확인하는 절차라던지, 그걸 컨트롤러단에서 똑같은 코드를 몇번을 반복하며 처리하고있었다.
정말 쓸데없는 짓이었다. 인터셉터로 단순히 메소드 하나만 띡 만들어주면 그 반복되는 몇줄 혹은 몇십줄의 코드가 깔끔하게 처리되는것이다.
AOP같은 경우에는 현재 프로젝트상 성능 확인이 필요한 부분 (캐릭터 상세 조회시에 한번에 10번 이상의 api를 호출함) 이라던가, 쨋든 공통적으로 처리되어야 하는 부분 (인터셉터와는 별개로)은 또 따로 메소드로 뺄 수 있지만, Util 클래스가 아닌 이상 주입을 시키기 애매한 클래스에서 동작하는 메소드가 존재할 때도 있다.
이럴때 AOP를 사용하여 적절한 행동을 실행하도록 정의 하고, 어노테이션만 붙여주면 공통적으로 실행하기 때문에 관리가 편해진다.
어드바이스 또한 글로벌 예외처리로 매번 꾸준히 컨트롤러에서 캐치를 해준다거나 할 필요 없이 따로 클래스 하나만 생성해 각 예외를 어떻게 잡을지만 처리한다면 문제가 해결된다.
공통적으로 공통된 행동을 분리시킴에 있어서 효과적인 방안을 제시해준다.
이래서 본인이 사용하는 프레임워크에 얼마나 이해도를 갖고있느냐가 결국 유지보수를 하기 쉬운 코드를 만들 수 있는 지름길이 된다.. 공부가 답이다.
'객체지향'은 정말 코드를 짜면 짤수록 내가 더 부족함을 느끼게 하는 단어이다.. 나태해지지말고 꾸준히 해야겠다.
별개로 몇달동안 내가 코딩인지 코딩이 나인지 모를정도로 살아왔는데.. 점점 한계가 오는거같다. 이대로 가는게 맞는것일지.. 방안을 찾아봐야겠다.
캐릭터 정보 불러올시에 스프링 Async 기능을 이용해 비동기처리로 받아오도록 구현했다. (속도가 조금 빨라졌으려나..? 실감이 되지 않아서 불안정한 작업을 할 바엔 돌려놓는게 좋겠다라는 생각으로 두고보고있다. 하지만 캐릭터 정보는 내가 처리하는 데이터가 아닌 외부에서 안전한 데이터를 가져오는것이니.. 아직 확실하지 않다.)
오늘 겪었던 문제 🤔
해당 기능을 구현할때 보통 프론트쪽에서 세션스토리지에서 토큰정보를 가져와서 구현하는 글들을 많이 보았는데 나는 세션스토리지를 쓰지 않던 상황이라 로그인시에 세션에 로그인 정보를 저장하고 뷰의 헤더에 로그인 정보를 인풋밸류로 저장해 로그인을 안했을땐 anonymousUser 로 입력하도록 하고, 로그인시에는 계정이름을 입력하도록 한 후에 구독을 하도록 했더니 동작이 되었다.
오늘 해결한 오류 🤔
오늘의 배운점🤔
Sse (Server - sent - event) 를 알게되었다.
기존에 알림기능을 구현했지만, 실시간으로 알림을 전달하도록 하는 기능은 구현하지 못해서 약간 애물단지? 굳이 구현했어야했나? 하는 느낌으로 Notification 도메인이 남아있었다.
마찬가지로 생각해보니 구조상 알림이 생성되어야 하는 동작에서 컨트롤러단에서 서비스를 분리해 로직을 실행한게 아닌, 각 도메인 서비스별로 레포지토리를 주입시켜서 생성시키고 있었다. 이건 결합도가 높아질 수 있는 행동 (이런식으로 코드를 짜게되면 부품처럼 사용하지 못하게 된다. )
따로 알림 서비스와 컨트롤러를 생성하고 분리를 진행했다. 훨씬 깔끔하고 보기 좋았다.
마찬가지로 알림이 필요한 기능에 단순히 Sse 메소드만 집어넣으면 전송이 되었다.
거기에 +@ 로 부트스트랩의 toast 에 대해서 알게되었다.
이제 alert() 가 아닌 토스트에 메시지를 띄워도 되겠다는 생각이 들었다...... 굿
랜덤한 직업과 닉네임을 부여한 후 , 해당 캐릭터가 등록을 원하는 모험단 소속으로 생성되었는지 체크 후 등록하도록 구현
오늘 겪었던 문제 🤔
문제라기보다는 고민거리가 있었다.
해당 캐릭터의 레이드 데미지를 예측하기 위해선 스킬 계수(퍼센트 데미지) 정보가 필요한데, 게임사에서 제공해주는 데이터에서는 일정한 정보를 가진 데이터가 존재하지 않았다.
예를 들면 A스킬의 옵션 설명 : ~~~공격 12타 {value1} x 12 \n ~~범위 {value2} px 이런식으로 데이터가 불규칙하게 되어있었다.
해당 게임사가 그렇게 데이터를 관리한다면 곧 그 게임사에서도 어떠한 알고리즘? 로직? 을 구현해 최종 스킬 데미지를 계산이 가능할텐데 , 도저히 내 머릿속에서는 방법이 떠오르지 않았다.
첫번째 방법으로는 스킬 데미지 설명 옵션을 가져온 후 , 핵심 키워드 (데미지, 공격, 스킬 등) 가 담겨있는 문자열만 뽑아서 value를 더하거나 곱해 계산하려고 했었다.
해당 방법은 고정된 곱셈 수 , px(범위) 등등 여러 문제가 있어서 포기
해당된 키워드중 데미지와 관련없는 키워드를 모두 제거 후 value 더하기
해당 방법 또한 마찬가지로 최종 퍼센트가 타 사이트와 맞지 않았다.
방법은 뭐가 있을까 ..? 다른 계수를 정리해놓은 사이트에서 일일히 데이터를 가져와서 해결하기엔 비효율적이다.
마찬가지로 버프력 또한 내가 지금 해당 게임을 하지 않고있으니 계산방법을 전혀 몰라서.. 일단 1차적인 방법으로 나중에 도움이 될 수 있도록 캐릭터를 조회할때 착용 장비의 상세 정보도 가져오도록 변경했다.
장비에 보면 고정적으로 스킬 데미지 퍼센트를 증가해주는 옵션이 있거나, 버프스킬의 스텟이나 공격력 증가 옵션을 강화시켜주는 옵션이 존재했기때문에, 해당 옵션으로 총 증가 스텟/퍼센트를 가공해서 버프력을 계산하거나 데미지를 계산하도록 하면 될 것 같았다.
최적의 방안은 아니지만, 해당 문제를 해결하기 위해서 한단계 더 나아갔다! 라고 생각하고있다 :)
오늘 해결한 오류 🤔
오늘의 배운점🤔
JPA의 @SqlDelete 기능과 @Where 을 알게되었다.
첫 프로젝트를 진행할때, 삭제 기능을 단순히 db상에 데이터를 남기고 컬럼하나의 값만 바꾸는 soft delete 가 아닌 , hard delete로 진행을 했었고, 추후에 soft delete 로만 바꿨는데, 이때 jpa에서 제공해주는 메소드를 사용하지 않고, setter로 일일히 자식객체까지 컬럼값을 변경해주었었다.
@SqlDelete 어노테이션으로 간단히 Update 쿼리문을 실행해 자식객체까지 cascade 설정을 해주면 알아서 컬럼값을 변경해준다.
+@ 로 deletedAt 변수를 선언해 추후에 일정 기간이 지난 데이터는 완전히 삭제가 가능하도록 건수를 남겨두었다. (관리자 페이지에서 구현을 하거나 자동으로 처리가 가능한 기능을 활용하면 좋을거같은데, 아직 배운게 없어서 모르겠다. 고민을 많이 해봐야겠다 :)
@Where 같은 경우엔 데이터를 불러올때 설정한 값들로만 불러올수있도록 (예를들어 삭제가 false인 데이터만) 설정할 수 있으나, 현재 프로젝트 같은 경우엔 회원의 게시글이나 댓글 기록이 삭제된 게시글,댓글도 확인이 가능해야했기 때문에 Where 어노테이션을 사용하면 예외가 발생했었다. 따라서 큰 변경점 없이 SqlDelete 어노테이션만 사용하고 Where 은 따로 사용하지 않았다.
RestApi 에 대해서 한번 더 공부해보았다.
현재 진행하는 프로젝트에 컨트롤러와 맵핑되어있는 uri들이 모두 엉망이라는것을 알게되었다.
아직 자세하게 공부하진 못했지만, rest api 의 정해진 규약? 에 맞춰서 uri들을 모두 수정해주었다.
특히 어떠한 자원이 담겨져있는 (게시판,캐릭터,유저 등등) 곳에는 구분자를 써서 복수로 표현해주는 (characters, articles, users) 같은 규칙을 다 갖고있도록 변경해주었다.
대문자를 쓰지 않는것과 중간을 구분해줄때 '-' 를 사용해주는 것 등 여러가지를 신경써서 수정해주었다.
남은 소소한 목표
-> 캐릭터 랭킹 페이지 구현 (직업별로 명성, 게시판에서 언급된 횟수, 혹은 전체 캐릭터 중 몇순위인지 등등)
-> 모험단 등록 관련 특혜 (개인 프로필 아이콘 모험단 등록 캐릭터들중 골라서 변경하고, 특별한 모험단 등록 아이콘 부여)
-> 모험단 페이지 (캐릭터 타임라인 혹은 모험단 전체 타임라인)
-> 관리자 페이지 (공지사항 등록 및 시간이 지난 삭제 게시글,댓글 삭제처리 등등 )
-> 캐릭터 데미지/버프력 산출 공식 알아내기
-> 데이터 가져올때 예외처리 (1초 안에 가져올 수 있는 데이터 수가 초과했을때 스레드 슬립 등)