멀티 모듈 프로젝트에서 의존성 관리는 어렵다 경험 삼아 & 재미로 멀티 모듈 프로젝트를 하고 있는데 의존성 관리를 아래와 같이 해왔다 module-api, module-service 가 module-core에 의존하고 있는 중이다 얼마전 비대해진 root build.gradle의 dependencies를 세분화하여 각각의 모듈 build.gradle로 옮기는 작업을 진행했다 깔끔하게 다이어트 시키고 모듈 별로 필요한 라이브러리에만 의존할 수 있게 만들어 성공적이었다 현재 root build.gradle에서는 위에 지정해준 project(':module') 라인이 빠진 상태고 모듈 별 build.gradle의 dependencies에서 implementation project(':module') 형태로 관..
토요일에 정말 오랜만에 친구들과 술 한잔 하고 있었는데 긴급 연락이 왔다 뉴스 링크가 걸려 있었는데 마인크래프트에서 시작한 java logging framework의 문제에 관한 기사였다 밖이라서 바로 대응은 못 했는데 logback 쓰고 있던 게 기억나서 더 찾아보았다 원격으로 코드를 밀어 넣을 수 있어 엄청난 문제긴 한데 특정 버전 (2.0-beta9 ~ 2.14.1)에만 해당하는 문제다 다행히 log4j2의 1.x 버전과 logback은 해당 사항 없어서 별 다른 대응 없이 잘 지나갈 수 있었다 원인으로는 log4j-core 모듈이라는데 대충 찾아본 바에 따르면 유연성과 편의 기능을 위해 작성된 코드로 인해 SQL injection 마냥 코드가 들어와서 실행되는 게 문제라고 한다 개인적으로 진행하는..
Springboot + Gradle 조합을 사용한다면 소규모 이상의 프로젝트에서 적용할 수 있는 꿀팁이 있다 프로젝트 루트 경로에 있는 .gradle 폴더 안에 gradle.yml을 이용해 캐싱, 병렬 실행, jvm memory 설정을 줄 수 있다 gradle.properties 도 가능하지만 가독성 & 중복 제거 측면에서 yml을 쓰는 것이 낫다 더 멋들어진 설정이 있나 찾아봤었는데 대충 요 정도만 쓰는 거 같다 .gradle/gradle.yml org: gradle: daemon: true caching: true parallel: true jvmargs: -Xmx2048m configureondemand: true gradle.yml을 작성했다면 터미널에서 아래 명령어들을 이용해 프로젝트 전체 테스..
오랜만에 쓰는 일기다. 일기 체질은 아니기 때문에 개발자들의 회고 마냥 분기 별로 써봐야겠다. 어느덧 21년도 끝나가고 있다. 입사한 지 4개월째가 되었다. 다행히도 업무 강도가 높지 않아 따로 많은 공부를 할 수 있었다. 머릿속에 얼마나 남았을지는 모르겠지만 꽤나 많은 책을 읽었다. 매달 읽을 책을 정하고 읽은 날은 노션에 기록하고 있다. 노션의 캘린더가 꽉꽉 차면 괜스레 기분 좋다. 아래는 11월 맛보기. 노션에 기록 남기기는 9월부터 시작했는데 얼마나 공부했는지, 공부 계획을 세울 때 도움이 많이 되는 것 같다. 노션이 아니더라도 공부 기록 강추다. 9월에는 개정판 Real MySQL vol.1 다 읽었고, 클린 코드는 읽다가 말았다. Real MySQL은 친구 추천으로 읽기 시작한 책인데 평범한 ..
진행하고 있는 프로젝트에서 logging library로 log4j2를 사용하고 있다 후보로는 log4j, logback, log4j2 등이 있었는데 performance 결과를 비교한 것을 보고 바로 정할 수 있었다 log4j2는 비동기 방식으로 처리되기 때문에 다른 slf4j 구현체들을 압살 하는 성능을 가지고 있다 성능이 미친듯이 뛰어나기 때문에 오류 또한 logback에 비해 훨씬 덜 나는 건 보너스다 스프링 부트를 사용하는 경우, 설정 또한 크게 어렵지 않기 때문에 학습 & 성능 향상 목적으로 도입했다 JPA를 사용할 때 application.yml 설정에 아래와 같은 설정을 추가해주면 log4j2를 이용해 이쁘장한 형태로 쿼리가 나온다 spring: jpa: open-in-view: false..
이번 글에서는 최근 진행한 사이드 프로젝트에 관해 써보려 한다 게시글 관리를 위한 Restful API를 만드는 것이 목적이었다 최대한 빠르게 완성하는 게 목표였기에 공부 중이던 Vue.js는 사용하지 않고 이전에 사용해본 적이 있던 thymeleaf를 활용해 SSR로 빠르게 진행해보았다 굳이 ViewController를 쓴 이유는 완성해놓고 Postman으로 HTTP request를 날려 JSON 형태의 데이터만 받아보는 것뿐만 아니라 눈으로 보고 싶어서 화면을 만들었고, ViewController에서 WebClient를 통해 ApiController에 요청을 날리도록 만들었다 따라서 전체적인 흐름은 thymeleaf 화면 -> ViewController로 요청 -> ApiController로 요청으로..
테스트를 돌리는 방법에는 아래 사진과 같이 테스트 폴더 자체를 선택해 Run을 눌러 돌리는 방법이 있고 명령어를 통해 전체 테스트를 돌리는 방법이 있다 단일 모듈 구조라면 테스트 폴더로만 돌리는 게 쉽고 빠르다 멀티 모듈 프로젝트의 경우에도 폴더를 이용해 돌려도 되지만 명령어를 통해 돌리면 빌드 수행 후 전체 테스트 코드가 한방에 돌아가기 때문에 편의성 측면에서 더 낫다 특히나 --parallel 옵션을 줘서 병렬로 수행할 수 있게 만들 수 있다 ./gradlew test --parallel org.springframework.dao.DataIntegrityViolationException 테스트 코드 작성 중 만난 오늘의 에러다 소스 관리를 위해 Github Actions를 써서 빌드&테스트 수행 후 ..
JWT 기반 인증을 하는 프로젝트를 진행 중에 있다 시간이 부족하거나 작은 규모의 사이드 프로젝트라면 회원가입 관련 파트는 OAuth2를 이용해 간단히 처리할 수 있다 나는 기존에 했던 프로젝트에서 도메인만 남겨두고 설계부터 전부 바꿔 진행하는 중이기 때문에 재미 반 & 학습 목적 반으로 세세한 부분까지 전부 손을 대보고 있다 회원 CRUD는 HATEOAS, Redis 캐시 추상화 때문에 우여곡절이 많았지만 결국 끝냈는데 끝낸 후에 회원 아이디 & 비밀번호 찾기는 어떻게 진행할지 한동안 고민을 했다 지난 프로젝트에서는 구글링을 통해 어찌어찌 thymeleaf, ajax를 이용한 이메일 인증을 했었다 다만 인증 처리 과정도 어설펐고 재사용하기 힘든 코드가 만들어졌다 이번 파트에서 목적은 모던 자바 인 액션..
프로젝트에 도입하기 위해 Jwt에 관하여 구글링 해보면 Login까지의 구현은 여러 블로그에 상세하게 잘 나와있다 그러다 Session 방식이 아닌 Jwt 방식을 사용해 로그아웃은 어떻게 구현 가능할까? 라는 호기심이 생겼고 이에 대해서 찾아보니 Redis를 이용해 해결 하는 방법이 있는 것 같다 로그아웃 요청 흐름을 보면 아래와 같이 이루어진다 1. 로그아웃 요청 2. 레디스에 액세스 토큰 넣음 3. 사용자가 기존 토큰으로 요청 시 JwtFilter에 있는 validate 과정에서 redis로 해당 accessToken 있는지 확인 4. 레디스에 값이 있으면 요청 거부, 없으면 요청 승인 AuthService에 위치한 logout() method SecurityContext를 비우고 accessToke..
게시글 Table of Contents 게시글 단건 조회 Request CURL HTTPie Request HTTP Example Response Response Fields Response HTTP Example 게시글 다건 조회 Request CURL HTTPie Request HTTP Example Response Response Fields Response HTTP Example 게시글 추가 Request CURL HTTPie Request Fields Request HTTP Example Response Response HTTP Example 게시글 수정 Request CURL HTTPie Request Fields Request HTTP Example Response Response HTTP ..
상품 Table of Contents 상품 단건 조회 Request CURL HTTPie Request HTTP Example Response Response Fields Response HTTP Example 상품 다건 조회 Request CURL HTTPie Request HTTP Example Response Response Fields Response HTTP Example 상품 추가 Request CURL HTTPie Request Fields Request HTTP Example Response Response HTTP Example 상품 수정 Request CURL HTTPie Request Fields Request HTTP Example Response Response HTTP Examp..
장바구니 Table of Contents 장바구니 단건 조회 Request CURL HTTPie Request HTTP Example Response Response Fields Response HTTP Example 장바구니 다건 조회 Request CURL HTTPie Request HTTP Example Response Response Fields Response HTTP Example 장바구니 추가 Request CURL HTTPie Request Fields Request HTTP Example Response Response HTTP Example 장바구니 단건 삭제 Request CURL HTTPie Request Fields Request HTTP Example Response Respon..
주문 Table of Contents 주문 내역 조회 Request CURL HTTPie Request HTTP Example Response Response Fields Response HTTP Example 주문 추가 Request CURL HTTPie Request Fields Request HTTP Example Response Response HTTP Example 주문 단건 삭제 Request CURL HTTPie Request HTTP Example Response Response HTTP Example 관리자 전용 주문 다건 조회 Request CURL HTTPie Request HTTP Example Response Response Fields Response HTTP Example 주문..
회원 Table of Contents 회원 가입 Request CURL HTTPie Request Fields Request HTTP Example Response Response HTTP Example 회원 로그인 Request CURL HTTPie Request Fields Request HTTP Example Response Response HTTP Example 회원 본인 조회 Request CURL HTTPie Request HTTP Example Response Response Fields Response HTTP Example 회원 정보 수정 Request CURL HTTPie Request Fields Request HTTP Example Response Response HTTP Examp..
[OAuth2] SecurityConfig, OAuth2Config 설정 리팩토링 Spring Security를 이용하다 보면 자동화된 설정이 많아 정말 편리하지만 그럼에도 불구하고 커스텀 설정을 추가해야 하는 경우가 많다 이 글에서는 그중에서 OAuth2 Config에 관해 헤맸던 부분을 다시 ryumodrn.tistory.com 지난 글에서 이어지는 내용으로 리팩토링 후 만난 문제에 관해 남겨보려 한다 새로 작성한 코드로 테스트 해보기 위해 로그인 페이지에서 OAuth2 github 로그인을 시도했는데 스프링 시큐리티가 기본 제공하는 /oauth2/authorization/github 경로로 접근 시 404 에러가 발생했다 또한 가독성을 위해 SecurityConfig에서 OAuth2SecurityC..
Spring Security를 이용하다 보면 자동화된 설정이 많아 정말 편리하지만 그럼에도 불구하고 커스텀 설정을 추가해야 하는 경우가 많다 이 글에서는 그중에서 OAuth2 Config에 관해 헤맸던 부분을 다시 정리해보려 한다 프로젝트가 크지 않다면 모든 보안 설정을 SecurityConfig 하나에 모아두는 선택은 나쁘지 않다 나는 Spring Security 공부를 하며 이것저것 시도해보느라 설정이 많아졌고 자연스레 그 설정에서 사용하는 Custom Bean들도 많아서 SecurityConfig에 다 때려박으면 스크롤이 길어져 가독성이 좋지 못하고 남들이 보기에 깔끔하지 못한 코드가 될 수 있을 것 같아 분리했다 나의 경우엔 아래와 같이 설정을 나눠놨다 최대한 세분화하여 나눴고 MethodSecu..
Spring Security를 사용할 때 권한 체크를 간단히 할 수 있는 방법은 URL 방식과 Method 방식이 있다 Method 방식은 AOP 방식을 기반으로 한다 특히 @EnableGlobalMethodSecurity 설정을 활성화해 아주 간단히 이용할 수 있다 해당 애너테이션을 찾아가 보면 활성화할 수 있는 요소들을 확인할 수 있다 Role만 지정해주는 간단한 사용을 위해서는 securedEnabled = true로 주면 되고 권한 검증에 SpEL을 사용하려면 prePostEnabled = true로 주면 된다 모든 설정은 default false이기 때문에 나는 간단한 방식과 SpEL을 사용하는 방식을 혼용하기 위해 두 설정을 켜줬다 아래와 같이 사용하고자 하는 설정을 켜주고 SecurityCo..
내 프로젝트에는 Member, Product, Cart domain이 존재한다 Cart는 Member를 N : 1 & Product를 1 : 1로 가지고 있는 Entity이다 이 상황에서 Cart를 응답하기 위한 CartResponse로 변환하고 GenericMapper를 그대로 쓰면 어떻게 될까? 당연히 Cart의 속성 member와 product가 그대로 나간다 변화 가능성이 큰 Entity로 응답하지 않기 위해 DTO 형태로 만들어 반환하는 것인데 현재의 DTO는 이를 위반한다 기존의 GenericMapper를 이용하며 member와 product도 dto 형태로 만들어 반환해줘야한다 약간의 커스텀 작업이 필요하다 우선 CartResponse에 Entity가 아닌 DTO 형태를 명시해준다 Gener..
지난 글은 https://ryumodrn.tistory.com/23 참고 한동안은 Redis @Cacheable에 대해 마음을 내려놓고 있었다 프로젝트 막바지에 적용해보다가 정 안 되면 List 형태로 반환하던지 jwt token 인증을 하고 있고 token은 page 형태로 반환할 필요가 없으니 token service에만 적용시켜 봐야겠다하고 생각 중이었다 이후 HATEOAS에서의 EntityModel, PagedModel을 공부하고 PagedModel의 구현에서 Cache를 적용할 수 있는 영감을 얻었다 PagedModel은 EntityModel의 Page 형태 반환이다 PagedModel의 구현을 살펴보던 중 deprecate된 생성자 메서드를 대신하는 static method인 of를 살펴봤다 ..
요즘 클린 코드와 유튜브 클린 코더스 강의를 보며 테스트 코드 작성을 연습 중이다 테스트 코드 개수가 늘어가면서 뿌듯하기도 하고 사람들이 왜 TDD에 열광하는지 알 것 같다 예전에는 테스트 코드 없이 바로 로직을 작성하고 문제가 생기면 스택 트레이스를 읽고, 디버거를 통해 문제 해결을 했었다 그런데 지금 진행하는 프로젝트는 4개의 백엔드 모듈과 1개의 프런트엔드 모듈로 구성되어 있어 규모가 작지 않다 이런 경우에는 혼자 진행하더라도 의존성이 어디까지 미칠지 완전히 파악할 수 없다 서비스 계층의 로직을 변경했을 뿐인데 여기저기에 영향을 미쳐 전체 로직이 흔들릴 수도 있다 - 백명석 님의 클린 코더스 강의, 최신 강의는 아니지만 굉장히 좋다! 강추 강추 https://www.youtube.com/watch?..