실수를 막아주는 좋은 개발: 역할 분리와 강제성
좋은 개발이란 실수를 예방하는 개발
실수를 막아주는 좋은 개발: 역할 분리와 강제성
좋은 개발 = 실수를 예방하는 개발

오랜 기간 여러 사람이 함께 만들어가는 서비스에서 ‘좋은 개발’이란 무엇일까요?
단순히 지금 당장 잘 동작하는 코드를 넘어, 미래의 나 혹은 동료 개발자가 실수하기 어렵게 만드는 개발이 바로 좋은 개발이라는 생각이 듭니다.
개발자의 주의력이나 문서에 의존하지 않고 시스템적으로 실수를 방지하는 개발. 이런 개발은 어떻게 가능할까요?
어느덧 개발된지 10년이 넘어가는 서비스를 유지보수하면서 느낀 점을 여러분께 풀어보려고 합니다!
👵🏼 고령의 코드와 암묵적 지식
개발자가 가장 두려워하는 것 중 하나. 서비스 장애입니다.
우리가 개발하는 서비스에서 발생하는 ‘장애’를 떠올려 봅시다. 그 원인을 파고들면 의외로 개발자의 아주 ‘작은 실수’에서 시작된 경우가 많습니다.
특히 여러 사람이 오랫동안 함께 만들어 온 회사 코드는 더욱 그렇습니다. 코드가 작성된 지 오래되어 “대체 이 코드는 왜 이렇게 짜여 있을까?” 하는 의도가 불분명한 경우가 많기 때문입니다. 이런 상황에서 작은 부주의는 예상치 못한 큰 장애로 이어지기 쉽습니다.
그렇다보니 미래의 동료와 나 자신의 실수를 시스템적으로 예방하는 것이 안정적인 서비스를 만드는 방법이라는 생각에 이르렀습니다.
실수를 예방하는 개발에는 크게 두 가지 원칙이 있습니다.
바로 객체지향설계와 강제성 부여입니다.
🕵🏼♀️ 명확한 역할과 책임 부여하기 (a.k.a. 객체지향)
🚨 ‘의식의 흐름대로’ 작성된 코드
혹시 코드를 작성할 때, 비즈니스 로직이 진행되는 순서대로 위에서부터 아래로 쭉 나열하는 방식으로 작성해 본 적 있나요?
아마 대부분 경험해보셨을 겁니다! 저만의 이야기라면 슬프지만😭 아무튼 저는 그랬습니다. 이런 ‘의식의 흐름대로’ 작성된 코드는 당장은 구현하기 편할 수 있지만, 기능이 추가될수록 코드가 수백 줄에 달하며 걷잡을 수 없이 길어집니다.
결국 이런 코드는 나중에 봤을 때 “도대체 왜 이 코드가 이렇게 짜여져 있는지, 왜 여기 if문이 있는지” 그 의도를 파악하기가 불가능한 수준이 됩니다.
💡 각자 자기 일을 하는 전문가 만들기
이 문제의 해결책은 객체지향의 핵심 아이디어인 ‘책임과 역할의 분리’에 있습니다. 복잡한 문제를 한 번에 해결하려 하지 말고, 여러 전문가에게 일을 나눠주는 것과 같습니다.
예를 들어 배달 앱을 만든다고 상상해 봅시다. 현실 세계에 가게, 메뉴, 주문이라는 개념이 있듯이, 코드에도 각각을 대표하는 객체를 만듭니다.
- 가게 객체: 가게 이름, 주소, 평점 등 자신과 관련된 데이터와 ‘가게 열기/닫기’ 같은 행동을 책임집니다.
- 메뉴 객체: 메뉴 이름, 가격, 사진 등 자신과 관련된 데이터와 ‘가격 변경하기’ 같은 행동을 책임집니다.
- 주문 객체: 주문 번호, 주문 메뉴 목록 등 자신과 관련된 데이터와 ‘주문 접수하기’ 같은 행동을 책임집니다.
이렇게 각 객체가 자신과 관련된 데이터(필드)와 행동(메소드)만 책임지도록 설계하면 다음과 같은 장점이 생깁니다.
- 높은 응집도: 관련 있는 기능들이 한곳에 뭉쳐있어 코드를 찾고 이해하기 쉬워집니다. ‘주문’에 관련된 로직을 찾고 싶으면 ‘주문 객체’만 살펴보면 되니까요!
- 낮은 결합도: 각 객체가 독립적으로 작동해서 하나의 기능을 수정해도 다른 곳에 미치는 영향이 적습니다. ‘메뉴’의 가격 정책을 바꿔도 ‘가게’나 ‘주문’ 로직을 건드릴 필요가 없는 거죠.
- 명확한 책임: 책임이 명확해지면 전체의 구조가 단순하고 예측 가능해집니다. 예를 들어, 컨트롤러는 외부 요청을 받아 DTO(데이터 전송 객체)로 변환하는 책임만 지고, 서비스는 각 도메인 객체들을 조합하여 비즈니스 로직을 실행하는 책임만 지며, ‘가격은 0원 이상이어야 한다’와 같은 핵심 유효성 검사는 도메인 객체 스스로 책임집니다. 이렇게 각자 할 일만 깔끔하게 처리하니 코드를 읽고 수정하기가 훨씬 쉬워집니다.
이렇게 각자에게 역할을 명확히 주는 것만으로도 코드는 훨씬 깔끔해집니다. 하지만 사람이 하는 일이다 보니 여전히 실수는 발생할 수 있습니다.
이런 실수를 원천적으로 막아주는 두 번째 원칙인 강제성에 대해 알아보겠습니다.
⛓️💥실수를 방지하는 안전장치 만들기 — 강제성 부여
⌨️ ‘문서’가 아닌 ‘코드’로 약속하기
우리는 협업을 위해 API 명세서, DB 테이블 명세서, 코딩 컨벤션과 같은 문서를 작성합니다. 하지만 문서는 시간이 지나면서 업데이트가 누락되거나 개발자가 바빠서 확인하는 것을 깜빡할 수 있습니다. 이렇게 사람의 기억력이나 부지런함에만 의존하는 방식은 잠재적인 실수의 위험을 안고 있습니다.
이러한 인간적인 실수를 줄이는 가장 좋은 방법은 규칙 자체를 코드에 녹여내어 지키지 않으면 아예 동작하지 않도록 강제하는 것입니다.
💡 방법 1: 인터페이스(Interface)로 ‘구현’을 강제하기
인터페이스는 ‘반드시 지켜야 하는 기능 목록을 담은 계약서’에 비유할 수 있습니다. 예를 들어, ‘데이터 저장’이라는 인터페이스를 만들고 그 안에 save()라는 메소드를 정의했다고 합시다.
이 인터페이스를 어떤 클래스가 구현하기로 약속했다면, 그 클래스는 반드시 save() 메소드를 만들어야만 합니다. 만약 개발자가 깜빡하고 이 메소드를 빠뜨리면 어떻게 될까요? 프로그램이 실행되기도 전인 컴파일 시점에 "계약 위반입니다!" 에러가 발생합니다. 덕분에 우리는 실수를 배포하기 전에 발견하고 수정할 수 있습니다. 이는 개발자의 부주의로 인한 장애를 막는 매우 강력한 안전장치가 됩니다.
물론 장점만 있는 것은 아닙니다. 인터페이스를 사용하면 실제 어떤 코드가 실행될지는 런타임에 결정되는 경우가 많습니다. 소스 코드만 봐서는 save() 메소드의 수많은 구현체 중 어떤 것이 호출될지 바로 알기 어려울 때가 있죠.
IDE에서 메소드를 보고 아 얘 구현체 뭐지 하고 들어가서 보려니까 두 개, 세 개, 많게는 열 개씩 리스트가 뜨기도 합니다. 자바나 스프링에서 구현체를 확인하다보면 쉽게 확인할 수 있는 현상이기도 하죠.
그래서 인터페이스를 활용할 때는 추적하기 쉽게 설계를 하거나, 필요하다면 문서화를 병행하는 지혜도 필요합니다.
💡 방법 2: DB와 코드를 활용해 ‘규칙’을 강제하기
서비스가 점점 확장되면서 여러 외부 업체(연동처)와의 연동이 필요해지는 상황을 떠올려 봅시다. 처음에는 연동처가 한두 곳이라 단순한 if-else 문으로 처리해도 큰 문제가 없었습니다. 하지만 A, B, C, …, G, H와 같이 연동처가 늘어날수록 조건문은 기하급수적으로 복잡해지고, 새로운 연동처가 추가될 때마다 코드를 수정해야 하므로 실수가 발생할 여지가 커집니다. 이런 상황에서는 연동 규칙을 코드 로직 속에 직접 작성하기보다는, 코드 바깥이나 코드 내부의 특정한 공간에 ‘규칙’을 정의하고 이를 기반으로 동작하도록 만드는 것이 효과적입니다.
이때 대표적으로 사용할 수 있는 방법이 두 가지 있습니다. 첫째는 DB 매핑 테이블을 활용하는 방식입니다. 연동처와 테이블 또는 로직의 관계를 데이터베이스에 정의해 두고, 애플리케이션은 이를 조회해 처리하는 구조입니다. 이렇게 하면 코드를 수정하지 않고도 DB 데이터만 변경하여 새로운 연동처를 유연하게 반영할 수 있습니다. 다만 어플리케이션 외부에 있는 DB와의 통신이 필요하기 때문에 약간의 시간 지연이 생길 수 있다는 단점이 있습니다.
둘째는 자바 Enum을 이용하는 방식입니다. 코드 내부에서 Enum을 통해 연동 규칙을 정의해 두는 것으로, 예를 들어 ‘신용카드’가 ‘카드그룹’에 속한다고 명시할 수 있습니다. 이 방식은 DB 조회 없이 코드 내부에서 바로 동작하므로 속도가 매우 빠르다는 장점이 있습니다. 하지만 Enum의 내용을 바꾸려면 코드를 수정하고 재배포해야 하기 때문에 변경이 잦은 데이터에는 적합하지 않습니다.
결국 상황에 따라 적절한 방법을 선택하는 것이 중요합니다. 예를 들어 뉴스의 카테고리처럼 한 번 정해지면 잘 바뀌지 않는 값이라면 Enum으로 코드에 고정하는 것이 속도 면에서 유리합니다. 반면 언제든지 추가되거나 변경될 수 있는 연동처 정보는 DB 테이블로 관리하는 것이 유지보수성과 유연성을 높이는 현명한 선택입니다.
👼🏼 개발자의 주의에 의존하지 않는 안정적인 시스템
지금까지 안정적인 서비스를 만들기 위한 두 가지 원칙(뇌피셜)을 살펴보았습니다.
- 역할과 책임 분리: 객체지향 원칙을 통해 코드의 구조를 명확히 하고 유지보수성을 높입니다.
- 강제성 부여: 인터페이스, DB, Enum 등을 활용해 사람의 실수에 의존하지 않고 코드 자체로 규칙을 강제합니다.
좋은 코드는 단순히 동작하는 것을 넘어, 미래에 발생할 수 있는 실수를 시스템적으로 예방하는 코드입니다. 객체 지향 원칙을 따르고, 인터페이스, DB, Enum 등을 활용해 강제성을 부여함하면 더욱 안정적인 서비스 운영이 가능합니다.
메타데이터
- post_id
- b74a2ef577a4
- slug
- 실수를-막아주는-좋은-코드-작성법-역할-분리와-강제성-b74a2ef577a4
- url
- https://medium.com/@sh940311/%EC%8B%A4%EC%88%98%EB%A5%BC-%EB%A7%89%EC%95%84%EC%A3%BC%EB%8A%94-%EC%A2%8B%EC%9D%80-%EC%BD%94%EB%93%9C-%EC%9E%91%EC%84%B1%EB%B2%95-%EC%97%AD%ED%95%A0-%EB%B6%84%EB%A6%AC%EC%99%80-%EA%B0%95%EC%A0%9C%EC%84%B1-b74a2ef577a4
- canonical_url
- https://medium.com/@sh940311/%EC%8B%A4%EC%88%98%EB%A5%BC-%EB%A7%89%EC%95%84%EC%A3%BC%EB%8A%94-%EC%A2%8B%EC%9D%80-%EC%BD%94%EB%93%9C-%EC%9E%91%EC%84%B1%EB%B2%95-%EC%97%AD%ED%95%A0-%EB%B6%84%EB%A6%AC%EC%99%80-%EA%B0%95%EC%A0%9C%EC%84%B1-b74a2ef577a4
- author_url
- https://medium.com/@sh940311
- status
- ok
- fetched_at
- 2026-07-13 06:23:13