전체 글

서버는 꺼지지 않아요
· 개발자취
SharedFileInputStream? A SharedFileInputStream is a BufferedInputStream that buffers data from the file and supports the mark and reset methods. It also supports the newStream method that allows you to create other streams that represent subsets of the file. A RandomAccessFile object is used to access the file data. Note that when the SharedFileInputStream is closed, all streams created with the..
멀티모듈 프로젝트를 구성하기 위해 프로젝트 패키지 루트 경로의 settings.gradle 파일에 각 프로젝트 디렉토리를 설정해줘야했다. 내가 만든 구조는 아래와 같았다. - boot - api-server - batch - kafka - core - domain - persistence 루트 패키지를 기준으로 쭉쭉 모듈들이 존재하는게 아니라 성격이 비슷한 모듈들을 하나의 디렉토리 내에 모아놓은 형태였다. 루트 패키지 내에 바로 존재하는 모듈을 include해주기 위해선 include 'foo' 처럼 해주므로, 느낌상 특정 디렉토리 하위에 있는 프로젝트는 include 'foo:bar' 처럼 해주면 될 것 같았다. 그래서 아래와 같이 설정했다. // settings.gradle rootProject.n..
· 개발자취
문제상황 사내에서 진행 중인 프로젝트의 테스트 코드와 관련된 문제는 꽤 오래전부터 체감되어왔다. 1. 테스트 코드 OOM(메모리 이슈) https://seongonion.tistory.com/140 에서 적은 바 있던 내용이다. 스프링 테스트코드 메모리(OOM)이슈 해결 아닌 회피기 어느 날 CI 파이프라인 중 테스트 실행 단계에서 지속적으로 OOM 에러가 나며 빌드에 실패하는 현상을 발견했다. 확인을 위해 로컬 환경에서 테스트 실행 시 현상이 재현되진 않았으나, 신기하게 seongonion.tistory.com 해당 문제를 분석하며 파악한 문제점은 다음과 같았다. @SpringBootTest 를 사용하는 테스트 코드들이 ApplicationContext 캐싱 hit rate가 낮아 매번 새로운 컨텍스트..
· 웹/Spring
우리는 스프링에서 테스트 코드 수행시간을 측정하기 위해 일반적으로 IDE의 도움을 받는다. 이미 많은 사람들이 사용하고 있는 JetBrains의 IntelliJ에서는 테스트의 수행시간을 화면 상에 표시해준다. 하지만, 이와 같은 방법은 몇 가지 한계점이 있다. 바로 @SpringBootTest 혹은 @DataJpaTest 등에서 테스트 컨텍스트를 로딩하는 시간이 수행 시간에서 제외된다는 것이다. 사실, 테스트 자체를 수행하는 시간은 1초 이상을 넘기는 일이 잘 없으며 테스트 컨텍스트 로딩 시간이 우리가 체감하는 테스트 수행 시간의 대부분을 차지한다. 이 시간을 줄이기 위해선 우선 해당 시간을 정확히 측정해볼 필요가 있다. 컨텍스트 로딩 시간을 포함한 테스트 코드 전체 수행시간을 구하기 위해선 스프링 프레..
· 개발자취
어느 날 CI 파이프라인 중 테스트 실행 단계에서 지속적으로 OOM 에러가 나며 빌드에 실패하는 현상을 발견했다. 확인을 위해 로컬 환경에서 테스트 실행 시 현상이 재현되진 않았으나, 신기하게도 swagger를 업데이트 해주는 openapi3 task 중 현상이 발생했다. (왜 그런지 아시는 분? ㅠ) 처음에는 테스트코드에서 Mocking을 위해 읽어들인 파일 쪽에 문제가 생겨 발생한 것으로 추측하였으나, 확인 결과 해당 코드는 큰 문제가 없음을 확인했다. (byte로 직접 쓰는 부분이 없었다) 다행스럽게도 로컬환경에서 재현이 되었기 때문에 Heap Dump를 떠서 누수 원인을 분석해볼 수 있었다. Heap Dump 확인 결과, org.hibernate.metamodel.internal.Metamodel..
· 웹/Spring
서론 Spring + JPA를 사용해 진행 중인 프로젝트에서 bulk insert가 필요한 상황이 생겼다. bulk라기엔 민망한 최대 300개 데이터이긴 하지만, JpaRepository의 saveAll 메서드를 호출해 저장 시 300개의 insert 쿼리가 따로 나간다는 점이 조금 마음에 걸렸다. 사실상 saveAll은 사용하지 말자는 것은 정해진 듯 보였고, 다음 고민거리는 대체자로 무엇을 사용할 것인가였다. 가장 일반적으로 사용되는 대안은 JdbcTemplate이었으나, 아예 문자열로 쿼리를 박아버리는 방식 자체가 찝찝해 다른 방법이 있는지 찾아보고 있던 상황이었다. 그러던 중 친구랑 나눈 대화가 실험욕구를 자극했다. 본론 그래서 JPARepository.saveAll과 JdbcTemplate.ba..
· 언어/JAVA
자바는 null값을 적절하게 처리하도록 하기 위해 Optional 이라는 Wrapper 클래스를 제공한다. Optional 클래스의 기능 중 orElse() 와 orElseGet() 은 모두 Optional로 가져온 값이 null일 경우, 해당 값을 무엇으로 대체할지 결정해주는 메서드이다. 지금까지 두 메서드의 차이점을 어렴풋이만 알고 정확히는 모른 채 Supplier로 써주기 귀찮다는 이유로 orElse()를 주로 사용해왔는데, 사실은 굉장히 위험하고 생각없는 짓이었다. orElse()와 orElseGet()은 모두 Optional의 값이 null인 경우 사용자가 지정한 다른 값을 넘겨주는 용도이다. 핵심적인 차이점은 파라미터로 받는 값이 T와 Supplier라는 것이다. 이게 뭐? 라고 생각할 수 있..
메일 서버 동작 구조 메일이 송신자를 떠나 최종 수신자에게 도달하는 대략적인 구조는 위 그림과 같다. 먼저, 송신자는 메일 송신 프로토콜인 SMTP를 통해 메일 데이터를 메일 발신 서버인 abc.com에 전송한다. 메일을 받은 abc.com은 메일 데이터에 포함된 수신자의 메일 서버의 IP를 DNS를 통해 조회하고, 동일하게 SMTP를 사용해 데이터를 해당 서버에 전송한다. 이렇게 전송된 데이터는 메일 수신 프로토콜인 POP3 혹은 IMAP를 사용해 최종 수신자에게 전달된다. 메일 송수신에 사용되는 프로토콜 1) SMTP (Simple Mail Transfer Protocol) 메일 송신을 위해 사용되는 프로토콜 사용 케이스 메일을 송신하고자 하는 사용자가 MTA(Mail Transfer Agent)를 ..
SeongOnion
조무래기 코딩