본격적인 이슈를 소개하기에 앞서 기능하나를 소개하려고 한다. 사용자 스토리 : 사용자는 관심있는 주식을 관심항목으로 등록할 수 있다. 간단한 기능이다. 이를 구현하기 위해 서버에서는 interest_asset 테이블을 설계했다. account_id 가 설정한 관심자산 목록은 콤마(,) 로 이어져서 데이터베이스에 저장한다. 예를들면 A 사용자가 52번, 418번, 3번, 17번 자산을 관심자산으로 등록하면 "52,418,3,17" 이런식으로 저장된다고 생각하면 된다. (각각의 데이터를 ROW 로 빼지 않고 이렇게 문자열 형태로 저장한 이유에 대해서는 해당 포스팅에서 소개하진 않는다! 이 포스팅에서 중요한건 그게 아니기 때문에 우선 넘어가자!) 관심자산 추가 API 의 로직은 이렇다. account_id 의..
Spring Security 는 Spring 으로 개발하는 어플리케이션 위에서 인증/인가를 구현하는데 도움을 주는 프레임워크다. 하지만 Spring Security 에 너무 의존적인 프로그래밍을 하거나 Spring Security 라는 프레임워크를 제대로 이해하지 못하고 사용하는 사례들을 너무 많이 접해 관련해서 내 생각을 정리해보려한다. 지극히 개인적인 생각이므로 너무 과몰입하지 않고 "이런 사람도 있구나" 정도로 가볍게 읽으면 좋을 것 같다. 내가 Spring Security 를 사용하는 다양한 사례들을 접하면서 느낀 문제점은 아래 3가지다. Spring Security 에 의해서 내가 만드는 서비스의 기능(비즈니스 로직)이 숨겨진다. Spring Security 을 제대로 이해하지 못하고 사용한다...
우리 팀 프로젝트에서는 JPA 와 QueryDSL 을 조합해서 사용하고 있다. 해당 프로젝트에서 JPA 를 통해 Batch Insert 하는 과정에서 겪었던 문제를 소개하고, 어떻게 해결했으며 그 결과로 어느정도의 성능향상이 이뤄졌는지 소개하려고 한다. Batch Insert 란? 간단히 설명하면 여러 개의 Row 를 한 번의 쿼리로 Insert 하는 방식이다. MySQL 은 Bulk Insert 방식을 통해 Batch Insert 가 가능하다. INSERT INTO MEMBER(NICKNAME, AGE) VALUE ('wiz', 29); INSERT INTO MEMBER(NICKNAME, AGE) VALUE ('chan', 27); INSERT INTO MEMBER(NICKNAME, AGE) VALUE..
사실 @Transactional 은 Spring Boot 로 진행한 첫 프로젝트부터 지금까지 정말 많이 써온 어노테이션이다. 지금까지 사용하면서 이해하고 있었던 바를 먼저 정리해보면 아래와 같다. 1. DB 에 접근하는 서비스 로직에 Transaction 을 적용하고 싶을 때 사용한다. 2. AOP 를 이용한 선언적 트랜잭션 방식이다. 3. Aspect 에서 PlatformTransactionManager 가 트랜잭션의 Commit, Rollback 을 처리한다. 이번에 회사에서 진행하고 있는 프로젝트에 @Transactional 의 propagation 속성을 이용해 Transaction 전파를 하는 과정에서 조금 더 제대로 알고 사용하고 싶은 마음에 정리해보려고 한다. 늘 회사 프로젝트에 새로운 기술..
분산락을 고려해야하는 상황은 무엇일까? Scale-out 을 통해 서버를 여러대로 확장한 상황에서 하나의 데이터에 대한 동시성 문제를 해결하기 위해 사용한다. 내가 동시성 문제를 겪은 상황은 API 서버를 여러 대로 Scale-out 한 환경에서 1개의 DB 서버를 공유하는 상황이었다. 부하분산을 위해 Scale-out 이 기본이되는 요즘 환경에서는 반드시 고려해야하는 부분이라고 생각한다. 프로젝트를 진행하면서 어떤 동시성 이슈를 만났고, 이를 해결하기 위해 어떻게 분산락을 사용했는지 살펴보려고 한다. 동시성 이슈를 경험하다! 위 테이블은 가상자산의 메타데이터 정보를저장하기 위한 테이블이다. "업비트" 와 같은 거래소에 신규 상장코인이 등장하면 그걸 식별해 로직상에서 currency 에 새로운 데이터를 ..
들어가기에 앞서.. 회사 프로젝트에서 API Request Header 에 포함되어 들어온 사용자정보를 Interceptor 에서 검증하고, 이를 UserContext 에 담아 전역적으로 사용하기 위한 구성을 했고, 이를 구현하는데 ThreadLocal 를 사용했다. Request Per Thread 방식으로 동작하는 Tomcat 기반의 서버였기 때문에 예상한대로 동작했다. WebFlux 에서 기본적으로 사용되는 Event-Loop 방식의 Netty 에서는 어떤 문제가 발생하고, 이를 어떤 방식으로 해결해 나갈수 있는지 고민한 과정을 포스팅에 담아보려고 한다. ThreadLocal 이란? ThreadLocal 은 쉽게 말해 Thread 별로 가지는 변수다. 내부적으로 Thread ID 를 Key 로하는 ..
개인적으로 스터디를 진행하는게 있는데 거기서 스터디 홈페이지를 개발하는 토이 프로젝트를 진행하고 있다. 그 과정에서 내가 고민한 부분과 그 것을 어떻게 해결했는지에 대한 경험을 이야기해보려 한다. 문제 내가 경험한 문제는 캘린더를 개발하는 과정에서 생긴 데이터 동기화 문제였다. 클라이언트 A가 캘린더 화면에 들어오면서 최초 로딩된 데이터를 가지고 온다. 그 상태에서 클라이언트 B가 데이터를 수정할 경우 클라이언트 A가 새로고침을 하거나 Polling 하는 등 새로 데이터를 받아오지 않는한 데이터를 동기화되지 않는다. 물론 사용자가 많지 않고 실시간 데이터 동기화가 중요하지 않은 곳이긴 했지만, "이게 실시간 서비스였다면 어땠을까?", "사용자가 많은 서비스라면 어땠을까?" 라는 생각으로 이 부분을 개선해..
Dependency org.springframework.boot spring-boot-starter-data-r2dbc io.r2dbc r2dbc-h2 runtime com.h2database h2 runtime Spring Data R2DBC 를 이용하기 위해서는 r2dbc-spi, r2dbc-pool, spring-data-r2dbc 의 의존성이 필요한데 spring-boot-starter-data-r2dbc 의존성 하나를 추가하면 이 모든 의존성을 한번에 추가해준다. 그리고 H2 Database 를 사용하기 위해서 관련 의존성을 추가해준다. Configuration @Configuration @EnableR2dbcRepositories public class R2dbcConfig extends Ab..
회사에서 캐시 구조에 대해서 리팩토링을 진행하는 과정에서 EhCache Self-Invocation 문제로 인해 캐시가 정상적으로 동작하지 않았던 사례에 대해서 포스팅하려고 한다. 이 문제가 무엇인지 예제를 통해서 간단하게 살펴보려고 한다. @Cacheable(value = "testCache") public String cache() { log.error("[Info] Create Data!!!"); return "hello, ch4njun"; } public String test() { log.error("[Info] test call!"); return cache(); } 위와 같은 구성이 있을 때 test() 의 내부에서 @Cacheable 어노테이션이 붙어있는 cache() 메서드를 호출한다. ..
Servlet 3.0 이전 Servlet 3.0 에는 Servlet Thread 만 존재했다. 예를들어 Thread per Request Model 을 사용하는 Tomcat 의 경우에는 기본적으로 200개의 Servlet Thread 를 가졌고 내부적으로 Blocking 되는 코드가 있다면 서버의 Latency 가 길어지게 된다. 왜냐하면 하나의 Request 에 대해서 하나의 Servlet Thread 를 할당하게 되는데, 해당 Servlet Thread 가 Blocking 상태에 빠지게 된다면 더 이상 Servlet Thread Pool 에는 남아있는 Thread 가 없게되고 Request 는 Servlet Thread 가 반환될 때까지 기다리게 되는 것이다. 이러한 문제를 해결하기 위해 Servle..