현 회사를 다니며 진행한 프로젝트 중에 동시성과 병렬성 문제를 만나고 이를 해결한 경험을 기록하고자 한다. 상황 해당 프로젝트에서 외부 API 서버에 어떤 값을 request하고 외부 서버에 의해 처리된 데이터를 response로 받는 일종의 어댑터 패턴의 서버 개념으로 동작하는 API 서버를 빌드하는 것이 목표였다. 이때 외부 API를 호출하는 기능은 제공하는 SDK를 이용해서 구현하였으나 한번의 API 호출에 하나의 값만을 전달할 수 밖에 없는 구조로 되어있어 성능이 썩 만족스럽지 못 했고, n건의 요청을 한 경우 n건의 응답이 반드시 와야 하는 consistency가 중요했다. 여기서 SDK API의 부족한 기능은 Java의 parallelStream을 통해 해결하였고 (병렬성) 100% 확실한 c..
JPA는 직접 동시성을 해결하기 보다는 DB의 동시성 제어 방법을 활용하는 방향으로 기능을 제공한다. 낙관적 잠금 (비선점 잠금) 트랜잭션 간에 충돌이 발생하지 않을 것이라고 기본적으로 가정하고 트랜잭션을 진행하는 방법을 말한다. @Entity public class Entity { @Id private Long id; @Version //int, Integer, long, Long, short, Short, java.sql.Timestamp 등과 같은 Type 사용 가능 private Integer version; } @Version이 적용된 필드가 존재하면 암시적으로 낙관적 잠금이 적용된다. 만약 Version 정보가 다른 경우 (dirty read가 발생한 경우) OptimisticLockExcep..
싱글턴 패턴 싱글턴 패턴이란 애플리케이션 내에서 하나의 클래스는 하나의 인스턴스만을 가지도록 강제하는 패턴이다. 하나의 클래스에서 하나의 인스턴스만을 가지므로 해당 인스턴스를 전역변수처럼 사용하여 접근하기 용이하고 인스턴스 개수를 조절할 수 있다. 구현 싱글턴 패턴은 생성자의 레벨을 private이나 protected로 두어 숨기고 static 메서드 (e.g. getInstance())를 통해서 인스턴스를 생성하는 식으로 구현될 수 있다. 예시 추상 팩토리, 팩토리 메서드, 빌더, 프로토 타입과 같은 패턴에서 싱글턴 패턴을 사용할 수 있으며 Logging이 가장 대표적인 싱글턴 패턴을 사용하는 예시이다. 싱글턴 패턴은 안티 패턴이다. 싱글턴 패턴을 이용하여 인스턴스를 전역 상태(Global state)..
Spring MVC는 다른 대부분의 MVC 패턴이 그러하듯 디자인 패턴 중 Front controller pattern을 적용하여 구현되어 있다. Front controller pattern은 한 요청이 들어오면 그 요청에 대한 응답을 할때까지 필요한 작업의 모든 것을 관장하는 역할을 하는 controller를 메인으로 두고 작업을 처리하는 패턴이다. '요청이 들어오면 응답이 나갈 때까지 작업이 진행된다'를 들으면 바로 떠오르는 단어가 하나 있을 것이다. Blocking IO. Spring MVC는 Blocking IO로 동작한다. 그렇기 때문에 각 요청마다 요청을 처리해줄 스레드를 만드는 것이고 이 처리를 좀 더 효과적으로 하기 위해 Thread Pool이 필요하게 되었고 IO heavy한 아키텍처에서..
Slf4j는 Simple Logging Facade for Java의 acronym으로 파사드 패턴을 이용한 로깅 인터페이스라는 의미 정도로 해석할 수 있을 것이다. Slf4j라는 파사드 인터페이스를 앞에 세워두고 Application과 로깅 프레임워크간의 직접적인 결합도를 없애고 파사드 하나만을 바라보게 해서 로깅 구현체가 어떤 식으로 변경되든 변화를 무시 혹은 최소화할 수 있다. System.out.println을 지양해야 하는 이유 성능을 떠나서 System.out.println는 콘솔창(표준 출력)으로 결과를 남기지만 로깅이 의미를 가지려면 파일의 형태로 저장이 되어야 하므로 로깅의 개념으로 사용이 불가능하다. (방법이야 찾아보면 가능하겠지만 성능의 오버헤드를 생각하면..) System.out.p..
당면한 문제는 아니지만 앞으로 WAS를 스케일아웃하면서 반드시 생기게 될 문제는 로그인한 유저의 세션 관리였다. 세션을 유지하는 방법에는 스티키 세션, 세션 클러스터링 등이 있지만 여러모로 트레이드 오프를 고려해봤을때 세션 클러스터링이 베스트 프랙티스라는 판단이 들었다. 스티키 세션은 처음에 연결된 세션으로만 유저를 계속해서 연결시키는 방법이며 세션간의 정합성을 유지시킬 필요가 없고 네트워크를 통한 오버헤드가 발생하지 않는다는 장점이 있지만 해당 노드가 죽었을때 노드에 붙어있던 세션들이 유실되며 로드밸런싱을 통한 이점을 얻지 못한다는 단점이 있다. 세션 클러스터링은 추가 서버와 메모리를 이용하여 세션의 정합성을 유지하고 세션을 관리하는 서버가 죽지만 않는다면 세션 유실에 대한 걱정을 할 필요가 없다는 장..
코드를 리팩토링하면서 대부분이 코드 스멜을 스멀스멀 풍겼지만 그 중에 최악의 코드 스멜은 for loop를 돌며 findById를 매번 해오는 코드였다. 코드를 리팩토링해버려서 대충 다시 작성해보자면 다음과 같은 코드였다. for (Long myId : myIds) { myRepository.findByMyId(myId); } for문을 돌면서 값을 하나씩 받아 매번 DB I/O를 발생시키는 코드였다..😞 해당 코드는 여러 검색과 질문을 통해 다음과 같은 코드로 변경시킬 수 있었다. List findByForeignIdIn(List foreignIds); JPA의 IN clause를 사용하는 아주 간단한 해결 방법이다. IN은 범위 조건문으로 BETWEEN, LIKE처럼 사용되지만 모든 값을 일일히 입력..
테스트 중인 클래스에서 이미 한 메서드의 로직을 검증했음에도 이곳 저곳에서 많이 불려서 쓰이는 경우에 해당 메서드를 모킹하여 복잡도를 줄일 수 있다. 해당 방법은 Partial Mocking이라 불리며 Spy Bean을 이용하는 방법이다. 테스트 대상을 해당 어노테이션의 순서에 맞게 설정한다.(반드시 Spy가 먼저 와야 한다.) @Spy @InjectMocks SellerService sellerService; @Spy와 @InjectMocks를 같이 쓰는건 위험하지 않을까? Each annotation has different purposes and they don't step on each other clearly as long as you need to use partial mocks. (a.k.a..
LocalDateTime이나 UUID, Random 등을 이용하는 경우 robust한 테스트를 위해서 값을 고정시킬 필요가 있다. 하지만 기본적으로 제공하는 mockito-core에서는 static 오버라이딩을 지원하지 않기에 다음과 같은 방법이 필요하다. Dependency static 메서드를 사용하기 위해 다음 dependency를 추가한다. (2022/06/19일 기준 4.6.1이 최신 버전이다.) 만약 아래의 디펜던시를 추가하지 않고 static 메서드를 모킹하려는 경우 다음과 같은 에러 메시지를 만날 것이다. The used MockMaker SubclassByteBuddyMockMaker does not support the creation of static mocks Mockito's in..
유닛 테스트 네이밍 컨벤션 테스트 네이밍 컨벤션을 따르는 것은 다른 코드 작성시의 컨벤션을 따르는 것만큼이나 장기적인 프로젝트를 진행하는데 중요하다. 널리 알려진 네이밍 컨벤션을 테스트에 적용함으로써 테스트의 이름만으로 어떤 테스트인지 이해할 수 있게 한다. 적절한 이름을 짓는 것은 어떠한 시도 적절히 번역할 수 없다는 점에서 시와 같다. ~W.H. Auden 네이밍 컨벤션을 선정하기 이전에 그 테스트가 왜 필요한지 테스트의 목적은 무엇인지 먼저 생각해보아야 한다. 테스트 네이밍을 하는데 다음과 같은 몇 가지 추천 방법이 있다. 테스트명은 특정한 필요조건을 명시해야 한다. 테스트명에는 기대되는 인풋이나 상태와 그에 상응하는 결과값을 포함시킬 수 있다. 테스트명은 워크플로우와 아웃풋을 명시하는 선언이나 사..
- Total
- Today
- Yesterday
- 도커
- RequestBody
- vim
- IDE
- neovim
- Dap
- JavaScript
- 레디스
- lunarvim
- RequestParam
- ModelAttribute
- 배포
- RequestPart
- 루나빔
- 아키텍처
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |