왜 나는 catch 없이 try와 finally를 사용하는가 (그리고 이것이 “Weird Code”가 아닌 이유)

발행: (2026년 4월 16일 AM 12:37 GMT+9)
6 분 소요
원문: Dev.to

Source: Dev.to

“catch가 없는” 느낌

catch 없이 try만 있는 코드를 보면 종종 완성되지 않은 느낌이 듭니다:

catch가 없는데, 왜 try를 쓰는 걸까?”

오랫동안 이 패턴은 이상하거나 심지어 잘못된 것처럼 보였습니다—하지만 조용히 발생한 프로덕션 버그가 왜 이것이 필수적인지 알려 주었습니다.

finally가 중요한 이유

대부분의 개발자는 세 부분 모델을 배웁니다:

  • try – 무언가를 시도한다
  • catch – 오류를 처리한다
  • finally – 끝에 실행된다

이 모델만 보면 catch가 없는 try는 쓸모가 없다고 쉽게 결론지을 수 있습니다.
그 결론은 틀렸습니다.

  • catch는 오류를 처리한다.
  • finally는 상태 복원을 보장한다.

이 두 책임은 서로 독립적입니다.

finally 없이 단순화된 흐름

if (hallwayInUse.current) return

hallwayInUse.current = true

const accessGranted = testSilentDoor(door)
updateStatusPanel(!accessGranted)

hallwayInUse.current = false

예외가 발생하거나, 프로미스가 거부되거나, return이 추가되거나, 드문 실패가 일어나면 공유 플래그가 절대 초기화되지 않습니다. 시스템은 조용히 교착 상태에 빠지게 되며—충돌도, 명확한 오류도 없고, 단지 멈춘 상태만 남습니다.

catch 없이 try … finally 사용하기

hallwayInUse.current = true

try {
  const accessGranted = testSilentDoor(door)
  updateStatusPanel(!accessGranted)
} finally {
  hallwayInUse.current = false
}

이 코드는 오류를 처리하려는 것이 아니라 계약을 선언하는 것입니다:

“이 작업이 실행되는 동안, 공유 자원이 잠겨 있다.
작업이 끝나면—어떤 방식으로든—그 자원은 반드시 해제되어야 한다.”

finally가 제공하는 보장

  • return이 발생해도 실행된다.
  • 예외가 발생해도 실행된다.
  • 비동기 작업이 거부돼도 실행된다.

예시: finally vs. finally 없음

// with finally
function patrol() {
  try {
    return
  } finally {
    releaseHallway()
  }
}
// without finally
function patrol() {
  try {
    return
  }
  releaseHallway() // ❌ 절대 실행되지 않음
}

“문” 비유

lockDoor()

try {
  enterRestrictedRoom()
  inspectDashboard()
} finally {
  unlockDoor()
}

문을 잠그고 작업을 수행한 뒤 반드시 문을 열어야 합니다—성공이든 실패이든, 중단이든 관계없이. 문은 “문을 열어야 할지 결정”하는 것이 아니라, 열림 단계가 보장되도록 할 뿐입니다.

언제 이 패턴을 사용해야 할까

정리하거나 상태를 복원해야 할 때는 catch 없이 try … finally를 사용하세요:

  • 락 해제
  • 플래그 초기화
  • 스피너 정지
  • 임시 상태 복원
  • 논리적 교착 상태 방지

전형적인 예시

setOperationActive(true)
try {
  runTask()
} finally {
  setOperationActive(false)
}
occupyHallway()
try {
  crossCriticalZone()
} finally {
  releaseHallway()
}

여전히 catch가 필요한 경우

다음과 같은 상황에서는 finally만으로는 충분하지 않으며 catch가 필요합니다:

  • 사용자에게 오류를 보여줘야 할 때
  • 실패에 따라 비즈니스 흐름을 결정해야 할 때
  • 대체 전략을 적용해야 할 때
  • 예외를 변환하거나 해석해야 할 때

이 경우 catch 블록이 반드시 필요합니다.

  • catch → 실패를 처리한다.
  • finally → 불변 조건을 보호한다.

하나가 다른 것을 대체하지 않습니다.

요약

다음 패턴

try {
  // main logic
} finally {
  // mandatory cleanup
}

이상하거나, 불완전하거나, 부주의한 코드가 아닙니다. 방어적이고 명시적이며 성숙한 코드입니다. finally를 “오류가 가는 곳”이 아니라 상태 보장의 장소로 바라보면 혼란이 사라지고, 미묘하고 비용이 많이 드는 버그로부터 시스템을 보호할 수 있습니다.

이 글이 프로덕션에서 조용히 발생하는 논리적 교착 상태를 하나라도 막는다면, 제 역할을 다한 것입니다.

0 조회
Back to Blog

관련 글

더 보기 »

React 초보자를 위한 기초

!React Basics for Beginners 표지 이미지 https://media2.dev.to/dynamic/image/width=1000,height=420,fit=cover,gravity=auto,format=auto/https%3A%2F%2Fdev-to-upl...