항해 플러스 백엔드 과정 2주차를 마치며, 제 경험과 성장 과정을 솔직히 공유하고자 합니다. 이번 주는 특히 TDD(테스트 주도 개발)와 클린 아키텍처, 그리고 동시성 문제에 깊이 파고들었습니다.
이번 주의 핵심 과제는 '특강 신청 서비스' 구현이었습니다. NestJS와 Prisma를 사용하여 클린 아키텍처 원칙을 적용하고, 동시성 문제를 해결하기 위해 노력했습니다. 이 과정에서 얻은 주요 성과는 다음과 같습니다:
클린 아키텍처의 실제 적용: Presentation, Application, Domain, Infrastructure 레이어를 명확히 구분하여 구현했습니다. 이를 통해 각 계층의 책임을 명확히 하고, 코드의 유지보수성을 높일 수 있었습니다.
TDD 실천: Jest를 사용한 단위 테스트와 통합 테스트를 작성하며 TDD를 실제로 적용해봤습니다. 테스트를 먼저 작성하고 구현하는 과정에서, 코드의 품질과 신뢰성이 향상되는 것을 체감할 수 있었습니다.
동시성 문제 해결 시도: Serializable 격리 수준을 적용하여 동시 요청 처리 시 발생할 수 있는 문제를 해결하려 노력했습니다. 이 과정에서 트랜잭션의 중요성과 데이터베이스 격리 수준에 대해 깊이 있게 학습할 수 있었습니다.
이 테스트는 40개의 동시 요청을 보내 정확히 30개만 성공하는지 확인하려 했지만, 실제로는 30개 미만의 등록만 성공하는 경우가 발생했습니다. 원인을 분석해보면, SQLite의 기본 격리 수준과 깊은 관련이 있음을 알 수 있었습니다.
SQLite의 기본 격리 수준은 'SERIALIZABLE'입니다. 이는 가장 높은 수준의 격리를 제공하지만, 동시에 성능 저하를 야기할 수 있습니다. 테스트 환경에서 발생한 문제의 원인은 다음과 같이 추정됩니다:
Lock 경합: SERIALIZABLE 격리 수준에서는 트랜잭션이 해당 데이터에 대해 독점적인 잠금을 획득합니다. 40개의 동시 요청이 발생할 때, 각 트랜잭션은 다른 트랜잭션이 완료될 때까지 대기해야 했을 것입니다.
데드락: 여러 트랜잭션이 서로 다른 순서로 리소스를 요청하면서 데드락이 발생했을 가능성이 있습니다. SQLite는 이를 감지하고 일부 트랜잭션을 강제로 롤백시켰을 수 있습니다.
타임아웃: 일부 트랜잭션이 lock을 획득하기 위해 너무 오래 기다리다가 타임아웃되어 실패했을 수 있습니다.
이러한 이유로 정확히 30개의 등록이 성공하지 않고, 그 이하의 등록만 성공하는 결과가 나타났을 것입니다.
이번 프로젝트에서 동시성 문제와 같은 복잡한 상황에 직면했을 때, 다양한 접근 방식을 시도하고 해결책을 찾아가는 과정이 특히 흥미로웠습니다. 이러한 문제 해결 능력을 더욱 발전시켜 어떤 기술적 도전에도 유연하게 대응할 수 있는 백엔드 개발자가 되고 싶습니다.
프로젝트 진행 중 currentRegistrations
필드를 도입하며 성능과 데이터 정합성 사이의 균형을 고민했습니다.
// Lecture 모델에 추가된 필드
model Lecture {
id Int @id @default(autoincrement())
title String
currentRegistrations Int @default(0)
maxRegistrations Int @default(30)
// ... 기타 필드
}
이 경험을 통해 데이터베이스 설계와 쿼리 최적화의 중요성을 깨달았고, 이 부분에 대한 더 깊은 학습이 필요하다고 느꼈습니다. 특히 대규모 트래픽 환경에서의 데이터베이스 성능 최적화 기술을 익히는 것이 다음 목표입니다.
항해 플러스 백엔드 과정 2주차는 TDD, 클린 아키텍처, 그리고 동시성 문제라는 중요한 주제들을 실제 프로젝트에 적용해보며 어려움도 많았지만, 백엔드 개발자로서의 역량을 키울 수 있는 시간이었습니다.