들어가며
오프라인 동의서를 온라인화 하는 작업에 참여했습니다. 이 과정에서 동의서 pdf 합성에 걸리는 시간을 10초에서 0.3초까지 줄인 경험을 공유하고자 합니다.
동의서 합성 과정 개요
동의서를 2개 싸인받아야 하는 상황을 가정해보겠습니다. 온라인 동의서 작성은 다음의 단계로 이뤄집니다.

사용자는 동의서1을 만나서 사인을 한 다음에, 동의서 2를 만나서 사인을 하고 최종적으로 저장하면 합성된 동의서 pdf를 받아볼 수 있게 됩니다.
이 때, 초기 구현에서는 [동의서 1 사인 -> 동의서 1 저장 -> 동의서 2 사인 -> 동의서 2 저장 -> 동의서 합성] 의 과정이 동기적으로 이뤄졌습니다.
또한 vm환경에서 1코어 성능 정도의 cpu를 할당받아서 사용하고 있어서 동의서 합성 부분에서 시간이 오래 걸려서 전체 과정에서 총 10초가량 시간이 소요되었습니다.
개선 1) 비동기화
첫 번째 개선 사항은 파일 저장 과정을 비동기화한 것입니다.
- 파일의 고유 seq를 DB에 기록하는 핵심 로직은 동기 방식으로 유지하고,
- NAS에 파일을 저장하거나 PDF를 합성하는 상대적으로 오래 걸리는 작업은 모두 백그라운드에서 비동기로 처리했습니다.

이 과정을 통해서 7초까지 시간을 줄일 수 있었으나, NAS에 파일 저장하는 부분에서 이상하게 시간이 7초 가량 걸리고 있었습니다.
개선 2) 사인 저장 로직 개선
NCloud를 사용하고 있습니다. ncloud의 경우 소량의 파일(40kb이내)은 25ms이내로 반환하도록 하는 스팩을 가지고 있습니다.
덧붙여서 ncloud를 사용하는 다른 저장로직의 경우 문제 없이 저장되는 것으로 보아 nas 저장 로직 자체의 문제는 아니라고 판단되었습니다.
디버깅을 하면서 오래걸리는 로직을 좁혀간 결과 파일명을 정하는 로직에서 문제가 발생함을 발견!
nowDate + "_" + RandomStringUtils.randomAlphanumeric(3) + ext;
3.15.0–3.16 버전의 RandomStringUtils.randomAlphanumeric 함수의 경우 서버 컴퓨터의 인피니티 풀에서 비트를 꺼내서 랜덤 문자를 정합니다.
인피니티 풀의 경우 컴퓨터 사용의 여러 이벤트들을 통해서 축적됩니다.
vm서버의 경우 일반 컴퓨터보다 이벤트 발생이 적다보니 인피니티 풀이 상대적으로 느리게 쌓입니다.
만약 인피니티 풀이 충분히 많지 않을 경우, 블로킹이 걸릴 수 있습니다.
자바에 내장된 random함수의 경우에는 인피니티 풀을 사용하는 것이 아니라, 고정된 seed(보통은 서버 시작 시간)을 사용함으로써 이 문제를 피할 수 있다고 합니다.
위 로직을 다음과 같이 바꾸었습니다.
return nowDate + "_" + generateRandomLetters(3) + ext;
...
private static final String ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
private static final Random RANDOM = new Random();
public static String generateRandomLetters(int length) {
StringBuilder sb = new StringBuilder(length);
for (int i = 0; i < length; i++) {
// ALPHABET에서 임의의 인덱스를 골라 한 글자 추가
sb.append(ALPHABET.charAt(RANDOM.nextInt(ALPHABET.length())));
}
return sb.toString();
}
이를 통해서 10초 -> 72ms까지 개선할 수 있었습니다.
3) 추가 개선이 필요한 상황
- pdf 합성 서버 분리
PDF 합성은 메모리를 많이 소모하는 장시간 작업입니다.
- 합성 중 메모리 부족이 발생하면 전체 서버 안정성에 치명적일 수 있습니다
- 합성 과정에서 문제가 생겨도 다른 기능은 정상 작동해야 합니다
따라서 PDF 합성 전용 서버를 분리해야 합니다.
- PDF 합성 로직을 인터페이스화하여 구현체 교체를 용이하게 설계
- 메인 애플리케이션과 분리해 장애 전파를 방지
- 이후 성능과 안정성을 위해 Java 대신 Go 기반 서버로 교체할 예정입니다
'개발' 카테고리의 다른 글
| 동시성 처리를 위한 락 종류 (0) | 2025.09.20 |
|---|---|
| 비동기 PDF 합성에서 트랜잭션 전파 실패 보완하기 (2) | 2025.08.05 |
| 메모리 성능 개선기 (2) | 2025.07.24 |
| 카카오 map api 클러스터링 속도 개선 (0) | 2025.02.21 |
| 에자일 방법론 경험기 (0) | 2025.01.05 |