1. 배경과 동기
이번 학습 목표는 자바 스레드와 동기화(synchronized)의 기초 개념을 직접 체험하는 것이었다.
제공되는 예제를 단순히 따라가는 대신, 현실에서 흔히 볼 수 있는 영화관 좌석 예매 시스템을 주제로 자유롭게 구현해 보았다.
처음 설계 단계에서 다소 막막했지만, 실제로 코드를 작성하고 버그를 수정하는 과정에서 오히려 더 깊이 몰입할 수 있었다.
2. 스레드와 동기화 기본 개념
- 스레드(Thread): 하나의 프로그램 안에서 동시에 실행되는 독립적인 흐름.
- Race Condition (경합 조건): 여러 스레드가 동시에 같은 자원을 읽고/쓰기 할 때 발생하는 충돌.
- 임계구역(Critical Section): 공유 자원에 접근하는 코드 블록. 반드시 한 번에 하나의 스레드만 실행되어야 함.
- synchronized: 자바에서 임계구역을 보호하기 위해 제공하는 키워드. 메서드 또는 블록에 붙여 동시 접근을 차단한다.

즉, "좌석 하나를 동시에 두 명이 예약하려고 하면 어떻게 막을 것인가?"가 이번 실습의 핵심이었다.
3. 구현 구조
클래스 설계
- App: 실행 진입점. Cinema를 생성하고 Customer 스레드를 실행 후 join으로 종료 대기.
- Cinema: 좌석 배열 관리 및 예매 처리. reserve() 메서드가 임계구역.
- Seat: 좌석 하나의 상태 보관. 예약 여부와 예약자 정보만 가진 단순 객체.
- Customer: 스레드. 랜덤하게 좌석을 선택해 Cinema.reserve()를 호출.
다이어그램
App → Customer(Thread) → Cinema(관리자) → Seat(엔티티)
클래스 다이어그램

시퀀스 다이어그램

4. 버그와 원인 분석
처음에는 최종 좌석 상태가 전부 [ ]로만 출력되는 문제가 발생했다.
증상

로그상으로는 성공했지만, 상태는 전혀 바뀌지 않았다.
원인

- Cinema.reserve()에서 좌석 상태를 변경하지 않고 단순히 true만 반환하는 분기문이 있었다.
- 경계 조건 검사에서 ||를 사용해, 실제로는 대부분의 경우 잘못된 분기로 빠져버림.
교훈
- 상태 변화가 실제로 코드에 반영되었는지 반드시 확인해야 한다.
- 조건식 하나(&& vs ||)로 전체 로직이 무너질 수 있다.
5. 해결과 개선
수정한 reserve() 핵심

- synchronized로 임계구역 보호
- 경계 조건 검사 수정
- Seat.reserve()를 실제로 호출하도록 로직 보완
결과

- 성공 로그와 최종 좌석 배치가 일치
- "이미 예약됨" 메시지가 정상 출력
- 동기화 제거 시 간헐적으로 중복 예약 발생 → synchronized 적용 효과를 체감
6. 몰입 경험
버그를 잡기 전까지는 "왜 좌석이 바뀌지 않을까?"를 계속 디버깅하며 코드를 뜯어보았다.
이 과정에서 단순히 코드를 짜는 수준을 넘어, 자원 공유와 동기화라는 개념을 몸으로 이해할 수 있었다.
특히, 로그는 성공이지만 실제 상태는 변하지 않는 모순 상황이 큰 인사이트를 줬다.
이후 synchronized 한 줄의 효과를 눈으로 확인하면서 몰입도가 크게 높아졌다.
7. 설계 접근에 대한 고민
처음에는 "어떤 클래스가 필요할지"조차 잘 보이지 않았다.
이번 경험으로 얻은 설계 접근 힌트는 다음과 같다:
- 현실 시스템을 단순화: 영화관 → 좌석, 고객, 매표소(Cinema)
- 역할 단위로 나누기: 실행(App), 상태(Seat), 관리(Cinema), 행위(Customer)
- 상호작용을 먼저 그려보기: "고객 → Cinema.reserve() → Seat"
- 상태 vs 동작 구분: Seat는 상태만, Cinema는 동기화/로직 담당
- 실험 가능성 고려: 동기화 전/후를 비교할 수 있게 설계
완벽한 설계가 처음부터 나오는 게 아니라, 단순한 역할부터 쪼개고 점점 구체화하는 방식이 접근하기 좋았다.
8. 배운 점과 다음 단계
- 스레드 동시성 문제는 "조건문 하나, 락 하나"로도 큰 차이가 난다.
- 로그가 성공이라고 해서 상태까지 성공한 것은 아니다.
- 설계는 현실 → 역할 → 상호작용 → 상태/동작 분리 순서로 접근하면 수월하다.
다음에는 아래의 아이디어를 추가해봐도 재밌을 것 같다.
- 재시도 로직: 실패 시 다른 좌석 자동 선택
- 선호 좌석 전략: 앞/가운데/통로 선호
- 출력 동기화: 로그 출력까지 깔끔하게 관리
9. 결론
이번 실습은 단순한 콘솔 기반 시뮬레이터였지만, 경합 조건과 임계구역의 본질을 체감하기에 충분했다.
스레드, synchronized, race condition이라는 추상적인 개념을 구체적으로 경험했고,
"설계 → 버그 → 원인 분석 → 수정 → 비교 실험"이라는 개발 사이클을 직접 경험한 점이 가장 큰 수확이었다.
'회고·TIL' 카테고리의 다른 글
| 개인 프로젝트 기획 및 설계 회고 – Campus Share (1) | 2025.10.14 |
|---|---|
| GET과 POST부터 JWT까지 — 웹 백엔드 인증 구조 회고 (0) | 2025.10.14 |
| 정보처리기사 합격 회고 (2025년 정기 기사 2회) (1) | 2025.09.22 |
| 클래스 다이어그램 기반 객체지향 실습 회고 (0) | 2025.09.09 |
| 2025년 상반기 TOPCIT 시험 회고 (6) | 2025.06.24 |