오늘도 끄적끄적

느리더라도 꾸준하게

3줄 요약

  1. OSIV가 꺼져있으면 트랜잭션이 시작될 때 엔티티 매니저가 생성되고, 트랜잭션이 끝날 때 엔티티 매니저를 종료한다.
  2. OSIV가 꺼져있고, 다른 트랜잭션이라면 엔티티 매니저가 공유되지 않기 때문에 엔티티 매니저의 1차 캐시도 서로 공유되지 않는다.
  3. OSIV가 켜져있으면 요청 당 엔티티 매니저는 한 번 생성되고, 뷰 렌더링이 끝날 때까지 엔티티 매니저는 종료되지 않고 트랜잭션이 다르더라도 1차 캐시가 공유된다.

들어가기에 앞서

엔티티 매니저 팩토리는 생성 비용이 비싸서 대부분 어플리케이션 당 하나를 생성하는 편이고, 엔티티 매니저는 생성 비용이 비싸지 않아서 어플리케이션에서 여러 번 생성된다.
하지만 엔티티 매니저는 쓰레드 세이프 하지 않기 때문에, 쓰레드 당 하나를 생성해야할 것 같고 Spring MVC는 리퀘스트 당 하나의 쓰레드가 할당되기 때문에 리퀘스트 당 하나의 엔티티 매니저가 생성될 것만 같은 기분이 든다.
나 또한 그렇게 알고 있었는데 아래 코드를 통해 뭔가 의문이 생겼다.

1
2
3
4
5
6
7
8
9
10
11
interface SomeRepository : JpaRepository<SomeEntity, Long>

@Service
class SomeService(
private val repository: SomeRepository
) {
fun some() {
val someEntity = repository.findById(1L)
val someEntity2 = repository.findById(1L)
}
}
더 읽어보기 »

3줄 요약

  1. @Transaction(readOnly = true)로 설정해도 트랜잭션은 시작된다. (transaction isolation level 보장)
  2. readOnly 트랜잭션도 시작한 트랜잭션을 종료시켜야하기 때문에 커밋도 한다.
  3. readOnly 트랜잭션의 Hibernate Session의 FlushMode는 Manual로 강제하기 때문에 트랜잭션을 커밋하기 전에 flush를 하지 않는다. (readOnly 보장)

@Transaction(readOnly = true)로 설정해도 트랜잭션은 시작된다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public interface SomeEntityRepository extends JpaRepository<Parent, Long> {
@Transactional(readOnly = true)
List<Parent> findByName(String name);
}

@Service
public SomeService {
private final SomeEntityRepository repository;

public SomeService(final SomeEntityRepository repository) {
this.repository = repository;
}

public void test() {
repository.findByName("qwer");
}
}
더 읽어보기 »

Netty를 사용하다보면 채널 파이프라인에 여러 이벤트 핸들러를 추가하기 마련이다.
그러다보니 순서가 중요할 때가 있다.

  1. 클라에서 보낸 데이터 중에 헤더를 파싱하고,
  2. 헤더에 따라 바디를 파싱하고,
  3. 바디를 토대로 뭔가를 또 처리해야하고…

이런 식으로 N 개의 이벤트 핸들러를 붙여야하고, 순서가 중요하다보니 어떤 순서대로 실행되는지가 궁금해졌다.

Inbound Event Handler

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class ExampleHandler1 : ChannelInboundHandlerAdapter() {
override fun channelRead(ctx: ChannelHandlerContext, msg: Any) {
println("1")
ctx.fireChannelRead(msg)
}
}

class ExampleHandler2 : ChannelInboundHandlerAdapter() {
override fun channelRead(ctx: ChannelHandlerContext, msg: Any) {
println("2")
ctx.fireChannelRead(msg)
}
}

class ExampleHandler3 : ChannelInboundHandlerAdapter() {
override fun channelRead(ctx: ChannelHandlerContext, msg: Any) {
println("3")
ctx.fireChannelRead(msg)
}
}
더 읽어보기 »

Netty는 왜 자바 표준인 NIO의 ByteBuffer를 사용하지 않는 걸까 이유를 몰랐는데 자바 네트워크 소녀 네티를 보고 이유를 알게되어 정리해봄.
ByteBuffer와 ByteBuf의 세부사항 보다는 ByteBuffer는 어떤 문제점을 가지고 있고, ByteBuf는 그 문제점을 어떻게 해결했는지에 초점을 맞추어 정리함.

ByteBuffer의 문제점

Netty의 ByteBuf는 자바의 ByteBuffer가 가진 문제점들을 해결하기 위해 나왔다.

데이터 쓰기/읽기 인덱스가 분리돼있지 않다

1
2
3
4
5
6
7
val byteBuffer = ByteBuffer.allocate(3) // 3바이트를 담을 수 있는 힙버퍼, 전부 0으로 초기화된다.
println(byteBuffer) // java.nio.HeapByteBuffer[pos=0 lim=3 cap=3]

byteBuffer.put(1)
println(byteBuffer) // java.nio.HeapByteBuffer[pos=1 lim=3 cap=3]

println(byteBuffer.get()) // 0
더 읽어보기 »

이벤트 루프의 개념이 명확하지 않아 자바 네트워크 소녀 네티를 보고 정리해봄.

통상적으로 이벤트 기반 어플리케이션이 이벤트를 처리하는 방식은 아래 두 가지가 존재한다고 함.

이벤트 리스너와 이벤트 처리 쓰레드 방식

브라우저에서 DOM에 클릭 이벤트를 어떻게 핸들링하는지 생각해보면 된다.

1
document.querySelector('body').onclick = e => console.dir(e) 
더 읽어보기 »

Netty의 개념이 하도 익숙하지 않아 자바 네트워크 소녀 네티를 보고 용어를 정리해봄.

Netty

Netty is an asynchronous event-driven network application framework
for rapid development of maintainable high performance protocol servers & clients.

네티는 비동기 이벤트 기반 네트워크 어플리케이션 프레임워크로써 유지보수를 고려한 고성능 프로토콜 서버와 클라이언트를 빠르게 개발할 수 있다.
즉, TCP 통신을 위해 무조건 Netty를 써야하는 건 아니지만 유지보수하기도 쉽고, 비동기 이벤트 기반이기 때문에 고성능도 보장하게 된다.
Spring Integration 또한 TCP 통신을 지원한다.

Spring Integration provides channel adapters for receiving and sending messages over internet protocols.
Both UDP (User Datagram Protocol) and TCP (Transmission Control Protocol) adapters are provided.

더 읽어보기 »

N줄 요약

SimpleClientHttpRequestFactory(RestTemplate을 기본생성자로 만들었을 때 사용하는)를 사용하더라도 내부에서 KeepAliveCache를 사용하여 커넥션 풀을 관리한다.
기본적으로 KeepAliveKey(protocol, host, port) 당 5개의 풀을 가지며 시스템 프로퍼티 http.maxConnections를 할당해주면 늘릴 수 있다.
커넥션 풀을 초과하면 커넥션은 바로 종료되며, 커넥션 풀 내의 커넥션은 매번 연결을 맺고 끊는 게 아니라 재사용 된다.
당연하게도 서버에서 Keep-Alive를 사용하지 않으면 매번 커넥션이 종료된다.
그럼에도 불구하고 SimpleClientHttpRequestFactory는 다음의 단점이 있기 때문에 토이 프로젝트가 아닌 이상 HttpComponentsClientHttpRequestFactory 같은 다른 구현체를 사용해야할 것 같다.

  • http.maxConnections라는 시스템 프로퍼티를 설정해야하는데 설정을 위해 자주 사용하던 properties(yml)에는 설정할 수 없다보니 다른 방법으로 설정을 해줘야하고, 그러다 보면 설정을 파악하려면 한 군데(properties 또는 yml)만 집중해서는 파악할 수 없는 내용도 있다보니 실수할 여지가 발생할 수 있다.
  • KeepAliveCache가 static 변수이다보니 서로 다른 SimpleClientHttpRequestFactory여도 동일한 커넥션 풀을 참조한다.
  • route(프로토콜, 호스트, 포트) 별 커넥션 풀은 설정할 수 있지만 토탈 커넥션 풀은 제한이 없다.

SimpleClientHttpRequestFactory가 뭐지??

RestTemplate의 기본 생성자를 사용하면 ClientHttpRequestFactory를 별도로 초기화하지 않으므로 기본값인 SimpleClientHttpRequestFactory를 사용한다.

더 읽어보기 »

Spring Boot Reference의 Testing - Detecting Test Configuration 파트를 보면 다음과 같은 내용이 나온다.

If you are familiar with the Spring Test Framework, you may be used to using @ContextConfiguration(classes=…​) in order to specify which Spring @Configuration to load. Alternatively, you might have often used nested @Configuration classes within your test.
When testing Spring Boot applications, this is often not required. Spring Boot’s @*Test annotations search for your primary configuration automatically whenever you do not explicitly define one.
The search algorithm works up from the package that contains the test until it finds a class annotated with @SpringBootApplication or @SpringBootConfiguration.

Detecting Test Configuration을 위해서 스프링에 친숙하다면 @ContextConfiguration이나 Nested @Configuration이 필요하다고 하고,
Spring Boot를 사용하면 @*Test(@SpringBootTest, @WebMvcTest, @DataJpaTest, etc.)에서 별다른 설정을 하지 않았다면 primary configuration을 찾아나간다고 한다.

N줄 요약

글이 길어지다보니 아무도 안 볼 거 같고, 집중을 하고 소스코드를 따라가면서 읽어야해서 우선 먼저 요약을 적어놓는다.

더 읽어보기 »

1
2
3
dependencies {
compileOnly("org.springframework.boot:spring-boot-starter-web:2.4.0")
}

runtime classpath에는 추가되지 않아서 원래는 java.lang.ClassNotFoundException이 나야 정상이다.
하지만 IntelliJ의 Spring Boot Configuration으로 실행하면 실행이 잘만 된다.

실제로 classpath를 찍어보면 아래와 같이 spring-boot-starter-web을 포함하고 있다.

더 읽어보기 »

JUnt 4

Field Injection 밖에 되지 않음.
Spring Boot 2.2.0부터 JUnit 5가 기본으로 탑재되기 시작했고,
Spring Boot 2.4.0부터는 아예 JUnit 4 의존성이 제거됐기 때문에 JUnit 4의 사용은 하지 말아야한다.

1
2
3
4
5
6
7
8
@RunWith(SpringRunner::class)
@SpringBootTest
class SomeTest {
@Autowired
private lateinit var a: SomeComponent
@Test
fun contextLoad() {}
}

JUnit 5

JUnit 5의 @ExtendedWith 어노테이션을 이용하면 테스트 전/후로 다양한 일을 할 수 있다.
@ExtendedWith 어노테이션은 어노테이션에 명시한 Extension들을 실행하는 역할 뿐이 하지 않는다.

더 읽어보기 »
0%