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을 작성했다면 터미널에서 아래 명령어들을 이용해 프로젝트 전체 테스..
진행하고 있는 프로젝트에서 logging library로 log4j2를 사용하고 있다 후보로는 log4j, logback, log4j2 등이 있었는데 performance 결과를 비교한 것을 보고 바로 정할 수 있었다 log4j2는 비동기 방식으로 처리되기 때문에 다른 slf4j 구현체들을 압살 하는 성능을 가지고 있다 성능이 미친듯이 뛰어나기 때문에 오류 또한 logback에 비해 훨씬 덜 나는 건 보너스다 스프링 부트를 사용하는 경우, 설정 또한 크게 어렵지 않기 때문에 학습 & 성능 향상 목적으로 도입했다 JPA를 사용할 때 application.yml 설정에 아래와 같은 설정을 추가해주면 log4j2를 이용해 이쁘장한 형태로 쿼리가 나온다 spring: jpa: open-in-view: false..
테스트를 돌리는 방법에는 아래 사진과 같이 테스트 폴더 자체를 선택해 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..
[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?..
이전 Redis 삽질기에서 해결법은 찾지 못 했으나 타협점을 찾았기 때문에 올린다 내가 의도 했던 것은 RestController 응답을 HATEOAS에 맞춰 ResponseEntity 또는 ResponseEntity로 내려주려고 했다 또한 응답 결과의 더 빠른 제공을 위해 spring-data-redis의 @Cacheable을 사용하려고 했다 그런데 EntityModel은 Serializable을 구현하지 않고, 독자적인 deserializer를 사용하고 있기 때문에 EntityModel에 @Cacheable을 사용한 방식으로 응답을 내려주면 아래와 같은 에러를 만날 수 있다 com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Type id ..
Springboot 프로젝트를 진행할 때 지루한 entity -> dto 변환 작업을 쉽게 도와줄 수 있는 mapping library들이 있다 대표적으로 model mapper, mapstruct, okari가 존재하는데 나는 성능 상의 이유로 mapstruct를 사용했다 mapping library의 성능 비교 글은 구글링을 하면 쉽게 찾을 수 있다 mapstruct는 롬복처럼 컴파일 시점에 annotation-processor에 의해 변환되기 때문에 성능이 가장 우수하다 다른 mapping library들은 런타임 시점에 리플렉션을 통해 매핑하기 때문에 성능에 부하가 올 수 있다 사용법은 okari는 성능이 가장 안 좋다고 하여 사용해본 적이 없어서 패스하고, mapstruct보다 model map..
개인 프로젝트에 OAuth2를 적용해봤다 Spring Security를 이용한 로그인, 회원가입 과정을 대체하기 위해서는 아니고 학습 목적이다 학습 목적이라 해보고 싶은 것 다 하려고 google, facebook, github, naver, kakao 다 연결시켰다 SpringBoot 프로젝트에 OAuth2 적용하는 과정은 자세히 설명해준 글을 첨부한다 - 1편 Spring Boot OAuth2 Social Login with Google, Facebook, and Github - Part 1 In this article, You'll learn how to add social as well as email and password based login to your spring boot applicat..
조회 성능 향상을 위해 Spring Data Redis를 도입했고 간편한 사용을 위해 캐시 추상화를 사용했다 @Cacheable, @CachePut 등을 사용한다 그런데 LocalDateTime을 변환할 때 살짝 문제가 있었다 가장 쉬운 해결 방법은 아래와 같이 변환될 형식을 지정해주는 것이다 LocalDateTime을 사용하는 모든 DTO에 annotation이 붙을 걸 생각하니 아찔해져 전역으로 형식을 바꿀 방법을 찾아봤다 아쉽게도 Redis, JacksonSerializer 에 대한 이해도가 낮아서 찾아본다고 했지만 아직 방법을 찾지 못했다 참고가 될 만한 블로그는 찾았는데 적용하지는 못 했다 Serializer / Deserializer를 Custom으로 만들어서 원하는 형식으로 파싱하고 Obje..
진행 중인 프로젝트 메서드에 slow query 분석 또는 잘못된 비즈니스 로직을 해결하기 위해 시간 측정 AOP를 활용해봤다 우선 aop 관련 package를 따로 만들어주고 시간 측정할 메서드에 적용시킬 custom annotation을 만들어 준다 AOP 활용을 위해 구글링 하면 보통의 블로그에서는 method level에 적용시키는 방법만 알려주고 있다 나는 단순히 몇 개의 메서드에만 적용시킬 게 아니라 코드 프리징과 배포를 하기 바로 직전까지 문제 해결을 위해 시간 측정을 계속할 계획을 가지고 있다 따라서 나와 같은 상황에서 시간 측정을 위해서라면 @LogExecutionTime 애너테이션을 모든 메서드에 달아줘야 한다 사실 애너테이션만 달아주면 해결되는 문제니 별 신경을 쓰지 않을 수도 있겠지..
멀티 모듈로 구성한 개인 프로젝트에서 빌드 시간이 지나치게 긴 것 같았다 test 시간에서 지연되고 있었고 우선 생각해 본 것은 @SpringBootTest를 Mockito를 이용한 단위 테스트들로 바꿔보려 했다 그러나 멀티 모듈로 나눴고 모듈 수만큼의 @SpringBootApplication을 가진 클래스가 존재한다 -> 21.09.24 현재는 api, batch 담당 모듈을 제외하고 나머지 @SpringBootApplication을 가진 클래스를 없애주었음 Test에서 @SpringBootTest를 제거하면 여러 개의 SpringBootApplication이 찾아진다고 하며 에러가 터졌다 @SpringBootTest(classes = "{Controller.class}, {Service.class}"..
지난 글 중에 Gradle Multi Module에 관한 글을 올린 적이 있다 이 글은 지난 글에서 이어지는 내용으로 QueryDSL을 사용하는 멀티 모듈 세팅을 계획하고 있다면 참고할만하다 멀티 모듈 세팅에 정답은 없는 것 같다 나는 아래와 같은 형태로 나누어 봤다 module-api -> Controller module-common -> Entity, Repository module-service -> Service, DTO 프로젝트 작성자가 편한 방식으로 구성하면 되는데 한 가지 주의할 점이 있다 바로 모듈 간의 관계성을 조심해야 하는데 기껏 멀티 모듈을 구성했는데 모듈 간의 관계가 깔끔하게 정립되지 않고 실타래처럼 엉켜있다면 그건 멀티 모듈로 구성한 의미가 없다 따라서 핵심은 모듈 간의 관계에 주..
zookeeper와 kafka 서버를 킨 후 consumer / producer를 통한 message queueing system을 연습 중이다 KAFKA_HOME은 https://kafka.apache.org/downloads 에서 다운로드한 binary file을 두는 위치이다. 맥북의 경우 터미널에서 cd 명령어로 다운로드한 폴더 안으로 들어가서 서버를 켜면 된다 일반적인 환경에서는 평화롭게 서버를 키고 진행하면 되는데 난데없이 아래와 같은 에러 메시지를 만났다 Connection to node 0 (/ip:9092) could not be established. Broker may not be available. 이전에도 동일한 문제가 있어 구글링을 통해 문제를 한번 해결했었는데 오늘 또다시 만나..
만약 Spring Cloud를 통해 MSA를 구성하고 있다면 Micro-Service 간 통신을 위해 RestTemplate 또는 FeignClient를 이용할 것이다 그럼 이 세 가지 방식 중 어떤 것을 택해야 할까? FeignClient는 Netflix에서 RestTemplate보다 효율적인 통신을 위해 만든 것이다 RestTemplate, FeignClient는 Synchronous, Blocking 방식이고 WebClient는 Asynchronous, NonBlocking 방식이다 R / F 하고 W는 결이 다르다 WebClient는 비동기 방식이니까 러닝 커브도 있을 것이고 간단한 토이 프로젝트에 WebClient를 적용시키는 것은 공부하려는 목적이라면야 좋지만 진검으로 사과 깎아 먹는 짓이다 ..