DRY(Don’t Repeat Yourself) Principle

DRY(Don’t Repeat Yourself) 는 소프트웨어 개발에서 중복된 코드, 데이터, 문서 등을 제거하고, 각각의 지식이나 로직이 시스템 내에서 유일하게 정의되도록 하는 설계 원칙이다. 이 원칙은 코드 재사용, 유지보수성 향상, 오류 감소, 일관성 확보 등 다양한 이점을 제공하며, 함수, 모듈, 컴포넌트, 설정 파일 등 다양한 수준에서 적용된다. 하지만 과도한 추상화나 무분별한 적용은 오히려 복잡성을 초래할 수 있으므로, 상황에 맞는 균형 잡힌 적용이 필요하다.

핵심 개념

DRY 원칙은 “Every piece of knowledge must have a single, unambiguous, authoritative representation within a system” (모든 지식은 시스템 내에서 단일하고 명확하며 권위 있는 표현을 가져야 한다) 라는 철학을 기반으로 한다.

핵심 요소:

코드, 데이터베이스 스키마, 문서, 테스트, 빌드 시스템 등 소프트웨어 전 영역에 적용되며, 중복을 제거하여 유지보수성을 높이고, 일관성을 보장하며, 오류 발생 가능성을 줄이는 것을 목표로 한다.
함수/메서드 추출, 모듈화, 컴포넌트화, 설정 파일 분리, 디자인 패턴 활용 등 다양한 기법으로 구현할 수 있으며, 무분별한 추상화나 조기 적용은 오히려 복잡성과 기술부채 (Technical Debt) 를 유발할 수 있으므로, 상황에 맞는 적용이 중요하다.
DRY 를 효과적으로 적용하려면 맥락을 고려하고, 점진적으로 리팩토링하며, 테스트로 변경 사항을 뒷받침하고, 공통점과 차이점을 명확히 식별해야 한다. 현대적인 소프트웨어 개발에서 DRY 는 여전히 중요하지만, 마이크로서비스, 분산 시스템 및 대규모 애플리케이션에서는 상황에 맞게 적용해야 한다.

배경

DRY 원칙은 1999 년 Andy Hunt 와 Dave Thomas 가『The Pragmatic Programmer』에서 공식적으로 정의했다. 이 원칙은 소프트웨어 개발에서 발생하는 지식 중복 (Knowledge Duplication) 문제를 해결하기 위해 고안되었다.

역사적 배경:

목적 및 필요성

주요 목적:

  1. 유지보수성 향상: 변경사항을 한 곳에서만 수정
  2. 일관성 보장: 동일한 로직의 일관된 동작 보장
  3. 오류 감소: 중복 코드로 인한 불일치 오류 방지
  4. 개발 효율성: 재사용을 통한 개발 시간 단축

필요성:

주요 기능 및 역할

항목설명
코드 중복 제거- 동일한 로직을 여러 곳에서 반복하지 않도록 방지
- 함수, 클래스, 모듈을 통한 추상화로 유지보수성 향상
지식 관리- 비즈니스 로직을 중앙에서 관리하여 일관성 유지
- 도메인 지식을 명확하고 일관되게 표현
변경 관리- 변경 시 단일 지점에서 수정 가능
- 전체 시스템에 대한 영향 범위를 최소화하여 안정성 확보

핵심 원칙

  1. Once and Only Once: 모든 지식은 정확히 한 번만 표현
  2. Single Source of Truth: 각 정보의 권위적 소스 존재
  3. Abstraction over Duplication: 중복보다는 추상화 선택
  4. Logical Cohesion: 논리적으로 관련된 요소들의 응집

주요 원리 및 작동 원리

graph TD
    A[중복 코드 식별] --> B[공통 로직 추출]
    B --> C[추상화 설계]
    C --> D[재사용 가능한 구성요소 생성]
    D --> E[기존 코드에서 참조]
    E --> F[유지보수 및 확장]
    
    subgraph "DRY 적용 과정"
        G[Before: 중복 코드] --> H[분석 및 식별]
        H --> I[리팩토링]
        I --> J[After: 단일 구현]
    end

작동 원리:

  1. 중복 식별: 유사하거나 동일한 로직 찾기
  2. 패턴 분석: 중복의 근본 원인 파악
  3. 추상화 설계: 공통 인터페이스나 베이스 클래스 설계
  4. 구현 통합: 단일 구현체로 통합
  5. 참조 변경: 기존 중복 코드를 새로운 구현 참조로 변경

구조 및 아키텍처

DRY 원칙을 적용한 시스템의 구조는 다음과 같다:

graph TB
    subgraph "DRY 아키텍처 구조"
        A[클라이언트 코드 1] --> D[공통 추상화 레이어]
        B[클라이언트 코드 2] --> D
        C[클라이언트 코드 3] --> D
        
        D --> E[핵심 비즈니스 로직]
        D --> F[유틸리티 함수]
        D --> G[데이터 모델]
        
        E --> H[데이터베이스]
        F --> H
        G --> H
    end
구성 유형구성 요소기능역할특징
필수추상화 레이어 (Abstraction Layer)공통 인터페이스 제공클라이언트와 구현체 분리인터페이스 안정성 유지
핵심 구현체 (Core Implementation)실제 비즈니스 로직 구현단일 소스로 기능 수행변경의 중심점
팩토리/헬퍼 클래스 (Factory/Helper Classes)객체 생성 및 유틸 기능 제공공통 기능 지원 및 유틸리티 제공재사용성 극대화
선택적설정 관리자 (Configuration Manager)시스템 설정 중앙 관리환경에 따라 설정 분리환경별 유연한 설정 관리
캐싱 레이어 (Caching Layer)성능 최적화시스템 부하 감소투명한 캐싱 제공

구현 기법

구현 기법정의구성목적실제 예시
함수 추출중복된 코드를 함수로 추출입력, 처리, 출력 구조코드 재사용성 향상calculateTax(amount) 함수
모듈화기능별로 코드를 분리하여 관리모듈, 인터페이스유지보수성 향상Python 패키지 구조
템플릿화반복 구조를 템플릿으로 정의템플릿 파일, 변수코드 중복 제거HTML 템플릿 (Jinja2)
클래스 상속공통 기능을 부모 클래스에 정의베이스 클래스 + 자식 클래스코드 재사용, 계층적 설계동물 → 개/고양이 클래스
컴포지션기능을 독립 컴포넌트로 조합독립 서비스 객체 조합유연한 객체 설계결제 시스템: 인증, 결제, 로깅 컴포넌트
설정 기반 프로그래밍설정 파일로 로직 외부화설정 파일 + 해석기동작의 유연한 변경JSON 기반 정책 설정 파일 관리
디자인 패턴 활용Singleton, Factory 등 패턴 적용패턴 구조코드 재사용, 결합도 감소DB 연결 관리, 객체 생성 관리
매크로 사용반복 코드 자동화매크로 정의, 호출자동화 및 중복 제거C 언어 #define
graph TD
    A[비즈니스 로직] -->|공통 로직| B[함수/모듈/컴포넌트]
    B -->|재사용| C[여러 기능/화면/서비스]
    D[설정 파일/문서화] -->|참조| C
    E[DB 스키마] -->|정규화| C

구현 기법 예시

  1. 함수 추출 (Function Extraction)

    • 정의: 중복된 코드 블록을 독립적인 함수로 분리
    • 구성: 매개변수화된 공통 로직
    • 목적: 코드 재사용성 향상
    • 예시:
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    
    // Before DRY
    function calculateTaxForEmployee(salary) {
        return salary * 0.2;
    }
    function calculateTaxForContractor(payment) {
        return payment * 0.2;
    }
    
    // After DRY  
    function calculateTax(amount) {
        return amount * 0.2;
    }
    
  2. 클래스 상속 (Class Inheritance)

    • 정의: 공통 기능을 부모 클래스에 정의
    • 구성: 베이스 클래스와 파생 클래스
    • 목적: 계층적 코드 재사용
    • 시나리오: 동물 클래스에서 공통 행동 정의, 개별 동물 클래스에서 특화된 행동 구현
    1
    2
    3
    4
    5
    6
    7
    
    class Animal:
        def speak(self):
            return "makes a sound"
    
    class Dog(Animal):
        def speak(self):
            return "barks"
    
  3. 컴포지션 (Composition)

    • 정의: 기능을 독립적인 컴포넌트로 분리하여 조합
    • 구성: 독립적인 서비스 객체들의 조합
    • 목적: 유연한 객체 구성
    • 시나리오: 결제 시스템에서 검증, 처리, 로깅 컴포넌트를 독립적으로 구성
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    
    class PaymentProcessor {
      constructor(validator, executor, logger) {
        this.validator = validator;
        this.executor = executor;
        this.logger = logger;
      }
    
      process(paymentData) {
        if (this.validator.validate(paymentData)) {
          this.executor.execute(paymentData);
          this.logger.log(paymentData);
        }
      }
    }
    
  4. 설정 기반 프로그래밍 (Configuration-Driven Programming)

    • 정의: 코드 로직을 설정 파일로 외부화
    • 구성: 설정 파일 + 해석 엔진
    • 목적: 코드 변경 없는 동작 수정
    • 시나리오: 비즈니스 규칙을 JSON 설정으로 관리
    1
    2
    3
    4
    5
    
    // discount_rules.json
    {
      "VIP": 0.2,
      "REGULAR": 0.1
    }
    
    1
    2
    3
    
    function getDiscountRate(userType, config) {
        return config[userType] || 0;
    }
    

장점과 단점

구분항목설명
✅ 장점유지보수성 향상변경사항을 한 곳에서만 수정하면 전체 시스템에 반영
일관성 보장동일한 로직이 일관되게 동작하여 시스템 신뢰성 향상
개발 효율성기존 구성요소 재사용으로 개발 시간 단축
오류 감소중복 제거로 인한 불일치 오류 방지
협업 개선팀원 간 공통 구성요소 사용으로 협업 효율성 증대
⚠ 단점과도한 추상화불필요한 복잡성 도입으로 코드 이해도 저하
조기 최적화패턴이 명확하지 않은 상태에서의 성급한 일반화
성능 오버헤드추상화 레이어로 인한 실행 시간 증가 가능성
결합도 증가공통 구성요소에 대한 의존성 증가
학습 곡선새로운 팀원의 복잡한 추상화 구조 이해 필요

도전 과제 및 해결책

도전 과제 항목설명해결책
추상화 레벨 결정적절한 추상화 수준을 찾기 어려움점진적 리팩토링, Rule of Three(세 번 반복되면 추출) 적용
성능과 유지보수성 균형DRY 적용이 성능 저하를 유발할 수 있음프로파일링 도입, 상황에 맞는 선택적 최적화
과도한 일반화 방지필요 이상으로 미래 요구사항을 고려한 추상화 설계YAGNI(You Aren’t Gonna Need It) 원칙 유지
도메인 이해 부족비즈니스 로직을 충분히 이해하지 못하고 추상화하는 경우 발생도메인 전문가와의 협업, 모델링 주도 설계 (DDD) 적용
팀 전체의 일관된 적용팀원 간 DRY 원칙에 대한 이해도나 해석 차이코드 리뷰, 페어 프로그래밍, 공통 코딩 가이드라인 정립
초기 설계 부담프로젝트 초기에 DRY 를 과도하게 적용하려는 경향애자일 방식의 점진적 개선, 초기는 명확한 중복 허용
코드 이해도 저하과도한 추상화로 인해 코드 가독성이 떨어지는 문제명확한 네이밍, 문서화 및 주석 강화, IDE 도움 도입

분류에 따른 종류

분류 기준종류설명
적용 범위코드 레벨 DRY함수, 메서드, 클래스 등 소스코드 단위에서 중복 로직 제거
예: 중복된 조건 분기 처리, 반복된 계산식 함수화
아키텍처 레벨 DRY모듈, 컴포넌트, 서비스 레이어 등 시스템 아키텍처 구성요소에서 중복 제거
예: 공통 API 레이어, 공통 유틸리티 컴포넌트
시스템 레벨 DRY마이크로서비스, DB 스키마, 메시지 포맷 등의 시스템 전체 단위에서 중복 제거
예: 공통 인증 서비스, 통합 메시지 포맷 모델
구현 방식추상화 기반상속, 인터페이스, 템플릿 등을 이용해 중복을 일반화하여 제거
예: Java 상속 구조, TypeScript 인터페이스 활용
컴포지션 기반재사용 가능한 모듈/컴포넌트를 조합해 중복 제거, 구조적 유연성 확보
예: React, Vue 컴포넌트 조합 방식
설정 기반외부 설정파일 (config, YAML 등) 을 통해 중복 동작/로직을 제어
예: Spring 의 application.yaml 설정, Terraform 변수화
추상화 정도최소 추상화명확한 중복에 대해서만 추상화 적용, 초기 설계 부담 낮음
예: 동일 로직이 3 번 이상 등장할 때만 함수화 (Rule of Three)
적극적 추상화잠재적/예상되는 중복까지도 사전 추출 및 공통화, 설계 복잡도는 높음
예: 도메인 공통 서비스 분리, 템플릿 기반 코드 생성

시스템 레벨 DRY

시스템 레벨에서 DRY(Don’t Repeat Yourself) 원칙을 적용한다는 것은 단순히 함수나 모듈의 중복 코드를 제거하는 것을 넘어서, 시스템 전반 (설계, 아키텍처, 인프라, API, 데이터모델 등) 에서 지식 (knowledge) 의 중복을 제거하고 변경을 단일화 (single-source-of-truth) 하는 것을 의미한다.

적용 영역중복 제거 대상적용 방식전략설명예시
아키텍처공통 로직/패턴/구조마이크로서비스, 공통 서비스, 도메인 분리공통 유틸리티 서비스인증, 파일 업로드 등 반복 기능을 공통 마이크로서비스로 분리AuthService, FileStorageService
도메인 중심 설계 (DDD)Bounded Context 로 도메인 분리 → 중복된 도메인 로직 제거주문, 결제, 사용자 도메인 분리
라이브러리 공유공통 로직을 공용 라이브러리로 분리하여 패키징@common/utils, internal-lib
API 설계요청/응답 구조 중복공통 DTO, Schema Registry공통 응답 구조API 응답 포맷을 status, message, data 로 표준화BaseResponse<T> DTO
스키마 재사용OpenAPI/Swagger 에서 공통 데이터 모델 정의UserDto, ErrorResponse
GraphQL 통합중복된 REST endpoint 를 GraphQL 단일 스키마로 통합getUser, getOrders → GraphQL Query
DB 설계데이터 구조 중복정규화, 참조 테이블, 뷰 (View)정규화중복 데이터 필드를 별도 테이블로 분리하여 참조 구조로 구성Address 테이블 분리
뷰 (View) 활용공통 집계 데이터나 요약 데이터를 View 로 생성v_user_summary, v_sales_monthly
ERD 일관성 유지공통 엔티티의 필드 구조 및 키 네이밍 규칙 표준화created_at, updated_at 필드 통일
인프라/배포IaC 코드 중복템플릿화 (Terraform, Helm)IaC 템플릿화공통 인프라 리소스를 코드 템플릿으로 추출하여 재사용Terraform Module, Helm Template
CI/CD 설정 중복재사용 가능한 워크플로우 구성CI/CD 재사용 템플릿공통 빌드/배포 파이프라인을 템플릿화하여 반복 제거reusable_workflow.yml (GitHub Actions)
배포 스크립트 중복단일화된 배포 명령 구성배포 스크립트 통합환경별 배포 스크립트를 하나로 통합하여 중복 제거deploy.sh --env=dev
로깅/모니터링로직별 로깅 포맷 중복표준 포맷 및 공통 핸들러 적용공통 로깅 인터페이스전체 서비스에 통일된 로깅 포맷 및 필드 구조 적용JSON 포맷, userId, traceId 포함
요청/응답 로깅 중복공통 미들웨어로 분리미들웨어화요청/응답 로깅을 미들웨어로 처리하여 반복 코드 제거Express, FastAPI 기반 LoggingMiddleware
테스트테스트 시나리오/데이터 중복테스트 유틸, 픽스처 재사용공통 테스트 유틸리티자주 사용하는 mock 데이터 생성을 함수로 분리create_mock_user(), mock_jwt_token()
테스트 픽스처 재사용다양한 테스트에서 재사용 가능한 JSON 등으로 구성된 고정 데이터 구조fixtures/user.json, fixtures/order.json

시스템 수준 DRY 적용 구조 예시

graph TD
  subgraph 공통 모듈
    A1[유틸 라이브러리] --> SVC1[Service A]
    A1 --> SVC2[Service B]
    A2[공통 인증 서비스] --> SVC1
    A2 --> SVC2
  end

  SVC1 --> API1[공통 API DTO]
  SVC2 --> API2[공통 API DTO]
  
  subgraph 인프라
    I1[Terraform Module: VPC]
    I2[Terraform Module: DB]
  end
  
  DEV1[Dev Environment] --> I1
  STG1[Staging] --> I1
  PRD1[Production] --> I2

분류에 따른 유형

유형특징 및 개요적용 예시 및 상황
함수형 DRY중복된 로직을 순수 함수 (Pure Function) 로 추출하여 상태 변화 없이 일관된 동작을 보장함
입력 → 출력에 집중하며, 부작용 (Side Effect) 을 제거함으로써 예측 가능하고 테스트 가능한 코드를 유도
수학 계산, 통계 처리, 데이터 변환, 로그 필터링 등예: JavaScript 의 map, filter 함수
객체지향 DRY클래스 상속 (Inheritance) 이나 컴포지션 (Composition) 을 통해 공통 로직을 구조화하고 재사용
공통 기능을 상위 클래스 또는 컴포넌트로 모듈화하여 유지보수성과 확장성을 확보
대규모 백오피스 시스템, ERP, CRM, 도메인 기반 설계 (DDD) 예: Java, C# 기반 웹 시스템
설정 중심 DRY코드 내에 하드코딩된 값 대신, **환경 설정 파일 (config)**이나 메타데이터를 통해 유연한 제어
여러 환경에 따라 동작을 쉽게 변경 가능하며, 코드 중복을 최소화
클라우드 배포, CI/CD, 멀티테넌시 시스템예: application.yml, .env, Terraform 변수
템플릿 기반 DRY반복되는 UI/로직을 템플릿 구조 (template system) 로 분리하여 중복 제거
동일한 구조의 반복적인 화면/코드를 공통 템플릿으로 처리
웹 페이지 렌더링, 이메일 발송, CRUD 생성예: Django Template, Jinja2, React 컴포넌트

설정 중심 DRY (Configuration-Driven DRY)

설정 중심 DRY코드 내의 반복되는 상수, 분기, 조건, 기능 구성 등을 코드 외부의 설정 파일 (configuration file) 로 분리하여 중복을 제거하고 유연성을 확보하는 접근 방식이다. 환경 변화나 기능 변경 시, 코드를 수정하지 않고도 설정 변경만으로 대응이 가능하게 설계한다.

목적:

구성 요소:

구성 요소설명
설정 파일YAML, JSON, XML,.env 등의 형식으로 작성된 외부 설정 파일
설정 파서설정 파일을 읽고 객체나 맵 형태로 로딩하는 기능 (예: Spring 의 @ConfigurationProperties)
설정 주입 매커니즘구성 요소나 서비스에 설정값을 주입하는 방식 (환경 변수, DI, 컨테이너 등)
기본값 처리 로직설정 누락 시 디폴트 값 처리 또는 오류 처리 로직 포함

예시:

1
2
3
# .env
PORT=3000
DB_HOST=localhost
1
2
3
4
5
6
7
// config.js
require('dotenv').config();

module.exports = {
  port: process.env.PORT,
  dbHost: process.env.DB_HOST,
};
1
2
3
4
5
6
// server.js
const config = require('./config');

app.listen(config.port, () => {
  console.log(`Server running on port ${config.port}`);
});

템플릿화 구현

템플릿화 (Templatization) 는 반복되는 코드 또는 UI 구조를 템플릿으로 추출하여, 중복을 줄이고 재사용성을 높이는 구현 기법이다. 주로 HTML UI, 이메일, 보고서, 코드 생성, SQL 쿼리 등에서 활용된다.

목적:

구성 요소:

구성 요소설명
템플릿 파일반복되는 구조와 변수를 정의한 틀 (예: product_card.html)
데이터 바인딩템플릿 내 변수에 동적 데이터를 주입
렌더링 엔진템플릿과 데이터를 결합해 최종 출력 생성 (예: Jinja2, Thymeleaf)
레이아웃 구조공통 템플릿을 상속하거나 포함하여 중첩 구조 구성 (예: base.html 포함)

예시:

실무 적용 예시

적용 영역적용 사례구현 방법기대 효과
백엔드 API공통 응답 포맷, 인증 처리공통 응답 객체 (BaseResponse), 미들웨어, 데코레이터 패턴일관성 확보, 유지보수 용이
프론트엔드공통 UI 컴포넌트 (버튼, 폼, 헤더 등)컴포넌트 기반 프레임워크 (React, Vue, Flutter 위젯)재사용성 증가, 개발 생산성 향상
웹 애플리케이션템플릿화된 레이아웃 재사용HTML 템플릿 엔진 (Jinja2, Thymeleaf) 활용코드 간결화, 디자인 일관성 유지
모바일 앱화면 간 공통 UI 위젯화Flutter 의 공통 위젯, Android Jetpack Compose 등화면 구성 재사용성, 코드 중복 최소화
데이터베이스공통 테이블 설계, 정규화, 공통 쿼리정규화된 ERD, View, 저장 프로시저, ORM 모듈화데이터 일관성 확보, 관리 비용 절감
설정 관리환경별 설정 파일 분리 및 공통화.env, application.yaml, 설정 계층화환경 독립적 운영, 유지보수 편의성
인프라/DevOps공통 빌드/배포 파이프라인 구성공통 CI/CD 템플릿 (GitHub Actions, GitLab 등)반복 작업 자동화, 설정 일관성 확보
테스트 코드공통 Mock 데이터 및 테스트 유틸리티픽스처 (fixture), mock 유틸 함수 분리테스트 코드 간결화, 반복 작업 제거
API 문서화공통 스키마 모델 정의OpenAPI, Swagger 스키마 재사용API 문서 자동화, 계약 일관성 확보
도메인 설계반복되는 도메인 로직의 서비스 분리DDD 기반 서비스 레이어, 공통 유스케이스 추출코드 중복 제거, 도메인 이해도 향상
로깅/모니터링공통 포맷/필드 로그 구성로깅 미들웨어, 구조화 로그 (JSON), traceId 활용분석 용이, 서비스 간 요청 추적 가능

DRY vs. KISS 원칙

항목DRY (Don’t Repeat Yourself)KISS (Keep It Simple, Stupid)
정의같은 지식은 시스템 내에 하나만 존재해야 한다설계나 구현은 가능한 단순하게 유지해야 한다
목적중복 제거, 재사용성 향상이해도, 유지보수성 향상
핵심 철학중복 제거단순성 유지
초점유지보수 시 변경의 최소화구현 및 이해의 용이성
리팩토링 방식공통 코드 추출, 추상화복잡한 추상화를 피하고 단순한 구현 유지
적용 예시함수/모듈 재사용, 템플릿화조건문 단순화, 비즈니스 로직 단순화
위험 요소과도한 추상화로 인한 복잡성 증가너무 단순하여 중복 또는 유연성 부족 초래 가능

충돌 가능 사례

사례문제해결 방안
모든 코드 중복을 제거해 공통 클래스를 깊이 있게 계층화DRY 는 충족하나 KISS 를 위반 → 코드가 지나치게 추상화됨중복을 일부 허용하더라도 가독성을 확보
간단한 if/else 를 템플릿화하지 않고 복붙KISS 는 충족되지만 DRY 위반공통되는 구조는 별도 파일로 분리하되 가독성 확보

SRP 와 DRY

SRP 의 적용은 책임을 분리하여 중복의 발생 지점을 명확하게 하며, DRY 는 중복된 로직을 추출하게 만들어 자연스럽게 책임이 응집되도록 유도한다. DRY 와 SRP 는 서로 다른 방향에서 시작되지만, 결과적으로 코드를 간결하고 유지보수 가능하게 만드는 목표는 동일하다. 실무에서는 두 원칙을 함께 고려한 설계와 리팩토링이 필수적이다.

항목SRP (Single Responsibility Principle, 단일 책임 원칙)DRY (Don’t Repeat Yourself, 중복 제거 원칙)관계 및 차이점
정의하나의 클래스/모듈은 오직 하나의 책임만 가져야 한다동일한 정보를 중복 없이 단일한 표현으로 유지해야 한다각기 다른 초점을 가진 원칙이나, 유지보수성 향상이라는 공통된 목표를 지님
목적변경 사유를 하나로 제한하여 코드의 응집도를 높이고 유지보수 용이성 확보중복 제거를 통해 재사용성과 코드 일관성을 확보SRP 는 구조적 설계에, DRY 는 로직/데이터의 반복 제거에 중점
설계 관점분리 (Separation of Concerns) 중심통합 (Abstraction, Reuse) 중심방향성은 다르지만, 결과적으로 응집도 향상, 변경 비용 감소라는 동일한 효과를 유도
실무 적용기능이 다른 책임을 가진 클래스는 분리 (ex: Controller vs Service)반복되는 로직은 함수/모듈화 하여 재사용SRP 를 적용하면서 중복 로직을 발견하면 DRY 리팩토링으로 자연스럽게 연결됨
리팩토링 계기하나의 클래스가 여러 기능을 담당하거나 변경 시 충돌 발생반복되는 코드로 인해 오류 발생 또는 변경 포인트 증가SRP 위반 → DRY 위반으로 이어지는 경우 많음
충돌 가능성거의 없음. 기능 분리로 인해 오히려 DRY 적용이 쉬워짐DRY 를 무리하게 추구하면, 하나의 함수에 너무 많은 책임 집중으로 SRP 위배 가능성 있음DRY 적용 시 SRP 도 반드시 고려해 책임 단일화와 로직 중복 제거의 균형 필요

예시

상황: 사용자의 주문 데이터를 저장하고, 저장 후 알림을 보내는 로직이 다음과 같다고 가정

1
2
3
4
def process_order(order_data):
    save_to_database(order_data)
    send_email_notification(order_data)
    send_sms_notification(order_data)
SRP 적용
1
2
3
4
5
6
7
8
class OrderService:
    def save(self, order_data):
        save_to_database(order_data)

class NotificationService:
    def notify(self, order_data):
        send_email_notification(order_data)
        send_sms_notification(order_data)
DRY 적용
1
2
3
4
5
6
7
8
class NotificationService:
    def _notify_via(self, method, data):
        # 공통 포맷팅, 로깅 등 중복 로직 제거
        method(data)

    def notify(self, data):
        self._notify_via(send_email_notification, data)
        self._notify_via(send_sms_notification, data)

이렇게 하면 SRP 로 책임이 분리되고, DRY 로 중복된 처리 로직도 제거된다.

활용 사례

사례 1: 전자상거래 플랫폼의 제품 필터링 시스템

시나리오: 온라인 쇼핑몰에서 제품 검색, 추천, 관련 상품 페이지에서 유사한 필터링 로직이 중복 구현되어 유지보수에 어려움을 겪고 있다.

시스템 구성:

graph TB
    subgraph "Before DRY"
        A1[검색 페이지] --> B1[검색 필터링 로직]
        A2[추천 페이지] --> B2[추천 필터링 로직]  
        A3[관련상품 페이지] --> B3[관련상품 필터링 로직]
    end
    
    subgraph "After DRY"
        C1[검색 페이지] --> D[공통 필터링 서비스]
        C2[추천 페이지] --> D
        C3[관련상품 페이지] --> D
        
        D --> E[필터 엔진]
        D --> F[정렬 엔진]
        D --> G[페이징 엔진]
    end

Workflow:

  1. 중복 식별: 각 페이지의 필터링 로직 분석
  2. 공통 인터페이스 설계: ProductFilterService 인터페이스 정의
  3. 핵심 로직 구현: FilterEngine, SortEngine, PagingEngine 구현
  4. 통합 서비스 개발: 모든 필터링 요구사항을 처리하는 단일 서비스
  5. 기존 코드 리팩토링: 각 페이지에서 공통 서비스 사용

역할:

사례 2: 전자상거래 웹사이트

시나리오: 전자상거래 웹사이트에서 제품 정보를 여러 페이지 (홈, 상세보기, 장바구니, 결제 등) 에 공통적으로 출력해야 하는 경우

시스템 구성:

구성 요소설명
웹 프레임워크Flask (Python 기반 경량 백엔드 프레임워크)
템플릿 엔진Jinja2 (Flask 내장 템플릿 렌더링 엔진)
데이터베이스PostgreSQL (제품 정보 저장)
ORMSQLAlchemy (DB 접근 추상화)
공통 모듈product_utils.py (제품 포맷팅, 가격 계산 등 로직 모듈화)
템플릿 구조product_card.html (공통 제품 카드 UI 를 위한 템플릿 컴포넌트)

활용된 DRY 전략:

시스템 구성 다이어그램

graph TD
A[Flask 라우팅] --> B["공통 함수 (product_utils.py)"]
A --> C["템플릿 렌더링 (Jinja2)"]
C --> D["공통 UI 템플릿 (product_card.html)"]
C --> E["개별 페이지 템플릿 (home.html, cart.html)"]
B --> F["DB 조회 (SQLAlchemy)"]
F --> G[PostgreSQL]

Workflow:

  1. 클라이언트 요청 → Flask 라우터로 전달
  2. 제품 ID 기반 데이터베이스 조회 수행
  3. 공통 함수로 제품 정보 포맷팅
  4. 제품 정보를 공통 UI 템플릿에 바인딩
  5. 여러 페이지에서 동일한 UI 구성으로 출력

역할 정리:

컴포넌트DRY 에 기여한 역할
product_utils.py제품 처리 로직을 단일화
product_card.htmlUI 컴포넌트 재사용
Flask + Jinja2템플릿 렌더링과 데이터 주입 자동화
ORM (SQLAlchemy)데이터 접근 코드 재사용

실무에서 효과적으로 적용하기 위한 고려사항 및 주의할 점

구분고려사항설명권장사항
설계추상화 수준 조절과도한 추상화는 오히려 이해도와 유지보수를 저하시킴3 번 규칙 (Rule of Three) 적용, 필요한 만큼만 추상화
미래 확장성 대비불확실한 미래를 대비한 과도한 일반화는 코드 복잡도를 증가시킴현재 요구사항 중심 설계, YAGNI 원칙 준수
구현공통 로직 위치 일관성여러 레이어에 흩어진 공통 로직은 재사용과 관리가 어려움전용 유틸리티/헬퍼 모듈에 통합 관리
함수/모듈 명명 규칙이름이 직관적이지 않으면 오히려 사용성과 가독성이 떨어짐명확하고 일관된 네이밍 컨벤션 적용
인터페이스 안정성추상화된 인터페이스가 잦은 변경에 노출되면 하위 모듈이 영향을 받음API 변경 최소화, 하위 호환 고려
성능 영향 고려DRY 적용 시 추상화 계층 증가가 런타임 성능에 영향을 줄 수 있음핫패스 (Hot Path) 는 선택적으로 최적화
유지보수문서화 필수추상화 구조가 복잡해지면 문서 없이는 사용하기 어려움공통 모듈에는 목적, 사용법, 예시를 포함한 문서화
테스트 커버리지 확보DRY 적용된 공통 컴포넌트에 대한 오류는 전체 시스템에 영향을 줄 수 있음단위 테스트, 통합 테스트 등 테스트 자동화 강화
팀워크코드 스타일/구조 표준화팀원 간 합의된 규칙 없이 추상화가 남발되면 코드베이스 혼란 초래코드 컨벤션, 공통 디렉터리 구조 정의
코드 리뷰 문화 정착개인적으로 적용한 DRY 가 팀 전체에 적합하지 않을 수 있음코드 리뷰 시 중복 제거뿐 아니라 과도한 추상화도 검토
DRY 적용 가이드 수립기준 없는 추상화는 오히려 유지보수를 어렵게 만듦DRY 적용 기준과 예외사항을 문서화하여 팀 공유

최적화하기 위한 고려사항 및 주의할 점

구분고려사항설명권장사항
메모리객체 생성 비용공통 로직을 객체화할 경우 다수의 인스턴스가 불필요하게 생성될 수 있음싱글톤 패턴 또는 객체 풀 (Object Pool) 적용
메모리 사용량추상화된 유틸리티나 헬퍼 모듈이 너무 방대해지면 메모리 누수 또는 불필요한 로딩 발생모듈의 경량화, 필요 시 Lazy Loading 적용
CPU/속도함수 호출/분기 비용DRY 로 추출된 함수가 짧고 자주 호출될 경우 오히려 호출 비용 증가 가능인라인 처리 또는 JIT 최적화 적용 가능
추상화 오버헤드깊은 추상화 계층 구조는 스택 깊이 증가와 오버헤드 유발꼭 필요한 경우만 계층화, 추상화는 2~3 단계 이내 권장
네트워크외부 서비스 호출 중복공통 API 호출 로직 중복 제거 후 너무 자주 호출될 경우 오히려 응답 지연 초래 가능호출 횟수 최소화를 위한 캐싱 또는 배치 처리 적용
데이터베이스중복 쿼리/데이터 접근재사용된 DAO/쿼리가 반복 호출되면 성능 저하 (N+1 문제 등) 유발공통 쿼리에 캐싱 도입, ORM 튜닝, 인덱스 최적화
캐싱연산 결과 캐싱고비용 로직을 함수로 추출한 경우 매번 계산 시 비효율적LRU 캐시, 메모리 캐시 (Redis 등) 전략 도입
렌더링템플릿 중첩템플릿 공통화로 중첩이 많아지면 HTML 렌더링 비용 증가템플릿 계층 제한 (2 단계 이하), 필요한 부분만 분리
의존성 관리유틸/공통 모듈의 종속성 증가모듈 간 순환 의존성 또는 의존성 폭증으로 인해 성능 저하와 테스트 어려움 유발모듈 경량화, 인터페이스 분리, 다이나믹 임포트 고려
코드 재사용과도한 DRY 적용성능보다 코드 재사용에 초점을 둘 경우 Hot Path 에 부정적 영향성능 Path 는 중복 허용, 필요시 별도 최적화 구현
모니터링성능 병목 지점 식별DRY 리팩토링 후에도 성능 저하 여부 지속 점검 필요APM 도구로 핫스팟 추적, 로깅, 메트릭 기반 측정 사용

대안적 접근

WET(Write Everything Twice)

핵심: " 반복을 허용하라 " 는 관점으로, 명확성과 독립성을 중시한다.
특징:

AHA(Avoid Hasty Abstractions)

핵심: " 성급한 추상화를 피하라 " 는 원칙.
특징:

DRY Vs 대안적 접근 (WET, AHA) 비교

구분DRYWETAHA
핵심 원칙중복 최소화, 추상화명확성, 독립성성급한 추상화 지양, 변화에 최적화
적용 시점반복 발견 즉시 추상화중복 허용, 필요 시 반복충분한 패턴 확인 후 추상화
장점일관성, 유지보수성, 재사용성명확성, 단순성, 독립성유연성, 올바른 추상화, 리팩토링 비용 절감
단점과도한 추상화, 가독성 저하, 잘못된 추상화유지보수 비용 증가, 에러 위험중복 허용 기간 존재, 추상화 타이밍 판단 필요
적합한 상황대규모, 반복적 로직소규모, 특수/테스트 코드변화가 잦은 영역, 요구사항 불확실 시

주제와 관련하여 주목할 내용

주제항목설명
DRY 원칙Single Source of Truth (SSOT)동일한 정보는 시스템 내 한 곳에서만 정의되어야 함. 일관성과 정합성 확보에 필수
Abstraction함수, 클래스, 모듈, 컴포넌트로 일반화 및 중복 로직 제거
Modularity기능별로 코드를 분리해 재사용성과 유지보수성을 높이는 구조적 전략
Code Centralization공통 로직을 하나의 중심 위치에 통합하여 변경 관리 단순화
Premature Abstraction필요 이상의 조기 추상화는 복잡성 증가와 유지보수 어려움을 초래하므로 주의 필요
DRY vs WET초기 개발 시 WET 허용, 반복이 3 회 이상일 때 DRY 적용 권장 (Rule of Three)
DRY vs SRPSRP 는 기능 분리를, DRY 는 중복 제거를 지향하지만 상호 보완적으로 작용
Microservice & DRY마이크로서비스 간에는 중복이 일부 허용될 수 있으며, DRY 적용 범위 조절 필요
대안 원칙KISS (Keep It Simple, Stupid)단순하고 명확한 설계를 유지함으로써 유지보수성과 가독성 향상
YAGNI (You Aren’t Gonna Need It)지금 필요 없는 기능은 구현하지 않음. 과도한 DRY 를 방지하는 방어적 개발 전략
AHA (Avoid Hasty Abstractions)성급한 추상화를 피하고, 반복이 충분히 관찰된 후에 일반화 적용
Rule of Three중복이 세 번 발견되면 리팩토링 시점으로 간주하는 실용적 기준
설계 패턴Template Method Pattern알고리즘의 골격은 유지하고 구체 구현은 하위 클래스에 위임하여 중복 제거
Strategy Pattern실행 중 전략을 교체할 수 있도록 하는 방식. 유연성과 DRY 동시 확보 가능
Factory Pattern객체 생성을 분리하여 코드 재사용성과 유연성 확보
Singleton Pattern동일 객체의 재사용을 통해 생성 비용 절감 및 중복 방지
구현 기법Composition over Inheritance상속보다는 조합을 통해 중복 제거 및 유연한 기능 확장 추구
Functional Decomposition작은 함수로 분해하여 공통 로직을 재사용함으로써 DRY 구현
Configuration Driven Design설정 파일로 공통 로직을 외부화하여 코드 중복 없이 동작 변화 가능
도구 및 기술Code Generator반복되는 코드 패턴을 템플릿 기반으로 자동 생성하여 생산성 및 일관성 확보
Static Analysis / Linter Tools중복 코드 탐지 및 코드 품질 자동 분석 (예: ESLint, SonarQube)
Metaprogramming코드 생성 또는 수정 로직을 코드 내에서 자동화하여 중복 방지
DSL (Domain-Specific Language)특정 도메인에서의 반복을 줄이기 위해 설계된 전용 언어 (예: Terraform, SQLAlchemy 등)

9. 하위 주제별 추가 학습 필요 내용

카테고리주제간략 설명
설계 원칙SRP (Single Responsibility Principle)책임 분리를 통한 중복 제거 유도. DRY 와 상호 보완적 설계 원칙
KISS (Keep It Simple, Stupid)과도한 추상화 지양, 단순한 설계를 강조하여 DRY 남용 방지
YAGNI (You Aren’t Gonna Need It)필요 이상의 일반화나 DRY 적용을 방지하는 방어적 개발 철학
SOLID 전체객체지향 설계 원칙으로 DRY 를 실현하기 위한 기반 원칙들
설계 패턴Template Method Pattern공통 로직을 상위 클래스에 정의하고, 하위 클래스에서 세부 로직만 정의
Strategy Pattern알고리즘 교체 가능성을 고려하여 중복된 조건 분기를 제거
Factory Pattern객체 생성 로직을 중앙화하여 중복 제거와 확장성 확보
리팩토링 기법Extract Method중복된 로직을 독립된 메서드로 추출하여 재사용성 확보
Extract Class관련 기능 묶음을 별도의 클래스로 추출하여 책임 분리 및 중복 감소
Pull Up Method상속 구조에서 중복된 메서드를 상위 클래스에 통합
Replace Duplicate Code with Template템플릿 메서드 패턴 적용을 통한 중복 제거
아키텍처 설계계층형 아키텍처 (Layered Architecture)프레젠테이션, 도메인, 데이터 계층을 나누어 관심사 및 로직 중복 제거
마이크로서비스 아키텍처 (Microservices)서비스 간 책임 분리를 통해 각 서비스 내부 중복 제거, 단 서비스 간 중복은 허용 가능
컴포넌트 기반 설계 (Component-Based Architecture)UI/비즈니스 로직 재사용 컴포넌트 중심으로 구조화
테스트 전략TDD (Test Driven Development)공통 로직과 기능을 테스트 관점에서 먼저 정의함으로써 DRY 를 강제적으로 유도
테스트 픽스처 관리재사용 가능한 테스트 데이터를 활용하여 테스트 코드 중복 제거
자동화 도구/기술코드 생성기 (Code Generator)반복되는 코드 패턴을 자동으로 생성하여 일관성과 생산성을 동시에 확보
정적 분석 도구 (Static Analysis Tools)중복 코드 탐지 및 코드 품질 향상 (예: SonarQube, ESLint)
DSL (Domain-Specific Language)특정 도메인 전용 언어로 공통 로직을 구성함으로써 중복 제거 (예: Terraform, dbt 등)
메타프로그래밍 (Metaprogramming)런타임 또는 컴파일 타임에 코드 생성/조작을 통해 중복 코드 제거

관련 분야별 추가 학습 내용

카테고리주제간략 설명
코드 품질SOLID 원칙객체지향 설계 5 대 원칙으로 DRY 와 함께 유지보수성과 확장성을 향상시키는 기반 철학
Clean Code중복 제거, 명확한 네이밍, 책임 분리를 통해 가독성과 품질 향상을 목표로 하는 개발 지침
Technical Debt코드 중복이나 임시방편 해결로 인해 누적되는 기술 부채의 식별과 관리
소프트웨어 공학Software Architecture레이어드 아키텍처, 마이크로서비스 등에서 DRY 를 시스템 차원에서 구현하는 전략
Design Patterns재사용성과 유지보수성을 높이기 위한 구조적 설계 패턴 (Factory, Template Method 등)
Refactoring중복 제거, 책임 분리 등을 위한 구조 개선 기술 (Extract Method, Inline Method 등)
개발 방법론Agile Development점진적 리팩토링과 반복적 개선으로 DRY 원칙을 실현하는 민첩한 개발 방식
Test-Driven Development (TDD)테스트 중심의 설계를 통해 중복된 로직을 미리 식별하고 추상화하는 접근 방식
DevOpsIaC(Infrastructure as Code), 배포 자동화 등에서의 DRY 구현 예 (템플릿화, 스크립트 재사용 등)
프론트엔드컴포넌트 기반 UI 재사용React, Vue 등에서 버튼, 폼 등 공통 UI 를 컴포넌트화하여 중복 제거
스타일 재사용 (CSS Modules, Tailwind)스타일 정의의 중복 제거 및 유지보수를 위한 패턴화 전략
백엔드RESTful API 공통 구조 설계응답 포맷, 에러 핸들링, 요청 파라미터 처리 등에서 중복 제거를 위한 구조화
인증/권한 공통 모듈화인증 로직, 권한 체크 등의 반복 처리 로직을 미들웨어나 유틸로 분리
데이터베이스정규화테이블 간 중복 데이터를 분리하고 참조로 관리하여 데이터 일관성과 무결성 확보
공통 뷰 및 저장 프로시저여러 쿼리에서 공통된 집계 로직을 뷰 (View) 또는 프로시저로 분리
테스트테스트 픽스처 재사용다양한 테스트에서 공통 데이터 구조를 재사용하여 테스트 코드 중복 제거
공통 테스트 유틸 함수반복되는 테스트 시나리오를 함수 또는 헬퍼 클래스로 추출
문서화API 문서 자동화 (Swagger, Redoc 등)중복 설명 제거, 공통 스키마 관리 등을 통해 DRY 적용 및 문서 품질 유지
인프라/배포IaC (Infrastructure as Code)Terraform, Helm 등에서 공통 인프라 구성 요소를 템플릿화하여 중복 제거
배포 파이프라인 재사용 (CI/CD)GitHub Actions, GitLab CI 등에서 워크플로우를 재사용 가능한 형태로 관리
협업 및 관리코드 리뷰중복된 코드 또는 과도한 추상화를 조기에 식별하고 팀 단위로 일관된 기준을 유지
팀 내 코딩 가이드라인DRY 적용 범위와 방식에 대한 팀 합의 기반 규칙 마련 및 문서화
성능 최적화캐싱 전략반복 연산 또는 데이터 접근 시 중복 호출을 줄이기 위한 Cache 활용 전략
동적 임포트/지연 로딩중복 로직 포함된 무거운 모듈의 초기 로딩 비용을 줄여 성능 최적화
대안 철학WET (Write Everything Twice)초기 개발 단계에서는 중복을 허용하며 가독성과 변경 용이성을 우선시하는 전략
AHA (Avoid Hasty Abstractions)성급한 추상화를 경계하고 실질적 중복이 발생할 때만 추상화 적용
Rule of Three세 번째 중복이 나타났을 때 추상화 또는 리팩토링을 시작하라는 실용적 기준

용어 정리

설계 원칙 및 소프트웨어 철학

용어설명
DRY (Don’t Repeat Yourself)중복 제거를 통한 코드 일관성과 유지보수성 향상 설계 원칙
WET (Write Everything Twice)중복을 허용하는 나쁜 코딩 습관으로, DRY 원칙의 반대 개념
KISS (Keep It Simple, Stupid)단순하고 명확한 설계를 지향하는 원칙
YAGNI (You Aren’t Gonna Need It)당장 필요하지 않은 기능은 구현하지 말라는 원칙
SOLID객체지향 설계 5 대 원칙 (SRP, OCP, LSP, ISP, DIP) 의 약어
GRASP책임 할당 중심의 설계 원칙 집합. SRP 와 상보적인 관계
Single Source of Truth단일 정보 원천만을 유지하여 일관성 있는 시스템 유지
조기 최적화 (Premature Optimization)필요 없는 시점에서 성능 최적화를 시도하는 비효율적 접근 방식

객체지향 및 모듈 설계 관련

용어설명
추상화 (Abstraction)핵심 개념만 표현하고 세부사항을 감추는 설계 기법
캡슐화 (Encapsulation)내부 상태와 구현을 감추고 인터페이스만 노출
상속 (Inheritance)상위 클래스의 속성과 동작을 하위 클래스가 물려받는 구조
컴포지션 (Composition)객체들을 조합하여 새로운 기능을 만드는 유연한 설계 방식
모듈화 (Modularity)시스템을 기능 단위로 나누어 재사용성과 유지보수성 향상
응집도 (Cohesion)모듈 내 기능들이 하나의 목적에 집중된 정도
결합도 (Coupling)모듈 간의 의존성 정도, 낮을수록 좋음
책임 (Responsibility)클래스나 모듈이 수행해야 할 기능이나 역할의 범위

소프트웨어 아키텍처 및 패턴

용어설명
마이크로서비스 (Microservices)각 기능을 독립적인 서비스로 구성하는 아키텍처 스타일
계층화 (Layering)시스템을 여러 추상화 수준으로 나누는 구조화 방법
Bounded Context도메인 주도 설계 (DDD) 에서의 명확한 의미와 책임 구역
템플릿 메소드 패턴알고리즘 구조를 상위 클래스에서 정의하고 구체적 구현은 하위 클래스에 위임
전략 패턴알고리즘을 캡슐화하여 런타임에 교체 가능한 구조로 설계
팩토리 패턴객체 생성 로직을 별도로 분리하여 유연한 객체 생성 처리
싱글톤 패턴클래스의 인스턴스를 하나만 유지하도록 보장하는 패턴
데코레이터 패턴객체에 새로운 기능을 동적으로 추가하는 구조 패턴
객체 풀 (Object Pool)재사용 가능한 객체를 미리 만들어서 성능 최적화 도모

코드 품질 및 도구

용어설명
리팩토링 (Refactoring)기능은 유지하되 코드 구조를 개선하는 과정
기술 부채 (Technical Debt)단기적 이익을 위해 쌓은 비효율적 코드로 인해 발생하는 유지보수 비용
정적 분석 도구 (Static Analysis Tool)코드 실행 없이 품질이나 오류를 분석하는 도구
프로파일링 (Profiling)프로그램 실행 시 성능 병목 지점 파악 도구
코드 리뷰 (Code Review)코드의 품질 향상과 오류 방지를 위한 동료 검토 과정
안티패턴 (Anti-pattern)단기적으로 효과 있어 보이지만 장기적으로 문제를 유발하는 잘못된 설계 방식
갓 오브젝트 (God Object)과도하게 많은 책임을 지닌 객체로, 유지보수에 불리함
복사 - 붙여넣기 프로그래밍동일 로직을 복사해서 사용하는 비권장 방식, DRY 위배

개발 및 운영 도구 관련

용어설명
템플릿 엔진HTML 등 템플릿에 데이터를 삽입해 최종 출력을 생성하는 도구 (예: Jinja2)
템플릿화 (Templatization)반복되는 출력 구조를 템플릿으로 추출해 재사용
메타프로그래밍코드가 다른 코드를 생성·조작하는 기법
코드 생성기반복적인 코드를 자동으로 생성하는 도구
런타임 / 컴파일 타임프로그램 실행 중의 시점 vs 컴파일 시점의 의미
배치 처리 (Batch Processing)여러 작업을 모아 한 번에 처리하는 방식
핫패스 (Hot Path)성능 최적화 대상이 되는 자주 실행되는 경로
API애플리케이션 간 상호작용을 위한 인터페이스
미들웨어애플리케이션과 서비스 간의 연결 역할을 하는 중간 계층 소프트웨어
픽스처 (Fixture)테스트에 사용되는 초기 데이터나 환경 상태
ORM (Object-Relational Mapping)객체지향 코드와 관계형 DB 간의 매핑 기술
저장 프로시저DB 에 저장된 재사용 가능한 SQL 코드 블록
컴포넌트 라이브러리재사용 가능한 UI 구성 요소 집합 (예: Material UI, AntD)
Polyrepo서비스별로 독립된 Git 저장소를 사용하는 전략 (↔ Monorepo)
IaC (Infrastructure as Code)인프라를 코드로 정의하고 관리하는 방식 (예: Terraform)
Terraform Module재사용 가능한 인프라 구성 단위, 모듈화된 IaC 요소

성능/데이터 이슈

용어설명
정규화 (Normalization)DB 에서 중복 제거 및 무결성을 확보하는 설계 방식
N+1 문제관계형 데이터 조회 시 반복적인 쿼리로 인한 성능 저하 문제
하위 호환성 (Backward Compatibility)새로운 시스템이 기존 시스템과도 잘 작동하는 성질

협업 및 개발 문화

용어설명
페어 프로그래밍두 명의 개발자가 함께 코딩하며 품질을 향상시키는 방법
코드 리뷰팀원 간 코드 품질 개선과 지식 공유를 위한 필수 절차

참고 및 출처

공식/학술적 정의 및 원칙 설명

온라인 실무 가이드 및 설명

기술 적용 사례 및 실습 자료

프레임워크 및 도구 관련 자료

커뮤니티 토론/비판적 시각


다양한 개발 영역에서의 DRY 적용

DRY 원칙은 코드 자체뿐만 아니라 소프트웨어 개발의 여러 측면에 적용될 수 있다:

데이터베이스 설계

정규화는 관계형 데이터베이스에서 DRY 원칙을 적용하는 방법이다.
이는 데이터 중복을 최소화하고 데이터 무결성을 보장한다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
-- DRY하지 않은 데이터베이스 설계
CREATE TABLE orders (
    order_id INT PRIMARY KEY,
    customer_name VARCHAR(100),
    customer_email VARCHAR(100),
    product_id INT,
    product_name VARCHAR(100),
    product_price DECIMAL(10, 2),
    quantity INT
);

-- DRY한 정규화된 설계
CREATE TABLE customers (
    customer_id INT PRIMARY KEY,
    name VARCHAR(100),
    email VARCHAR(100)
);

CREATE TABLE products (
    product_id INT PRIMARY KEY,
    name VARCHAR(100),
    price DECIMAL(10, 2)
);

CREATE TABLE orders (
    order_id INT PRIMARY KEY,
    customer_id INT REFERENCES customers(customer_id),
    product_id INT REFERENCES products(product_id),
    quantity INT
);

사용자 인터페이스 개발

컴포넌트 기반 UI 프레임워크 (React, Vue, Angular 등) 는 UI 요소를 재사용 가능한 컴포넌트로 추상화하여 DRY 원칙을 구현한다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
// DRY하지 않은 UI 코드
function UserProfile() {
    return (
        <div className="card">
            <div className="card-header">사용자 프로필</div>
            <div className="card-body"></div>
        </div>
    );
}

function ProductDetails() {
    return (
        <div className="card">
            <div className="card-header">제품 상세정보</div>
            <div className="card-body"></div>
        </div>
    );
}

// DRY 적용: 재사용 가능한 Card 컴포넌트
function Card({ title, children }) {
    return (
        <div className="card">
            <div className="card-header">{title}</div>
            <div className="card-body">{children}</div>
        </div>
    );
}

function UserProfile() {
    return (
        <Card title="사용자 프로필">
            
        </Card>
    );
}

function ProductDetails() {
    return (
        <Card title="제품 상세정보">
            
        </Card>
    );
}

테스트

테스트 코드에서도 DRY 원칙을 적용하여 설정, 데이터 생성, 단언 등에서 중복을 줄일 수 있다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
// DRY하지 않은 테스트 코드
describe('User service', () => {
    let database;
    
    beforeEach(() => {
        database = new Database();
        database.connect();
    });
    
    afterEach(() => {
        database.disconnect();
    });
    
    it('should create a user', () => {
        const user = { name: 'John', email: 'john@example.com' };
        const result = createUser(database, user);
        expect(result.success).toBe(true);
    });
});

describe('Product service', () => {
    let database;
    
    beforeEach(() => {
        database = new Database();
        database.connect();
    });
    
    afterEach(() => {
        database.disconnect();
    });
    
    it('should create a product', () => {
        const product = { name: 'Phone', price: 999 };
        const result = createProduct(database, product);
        expect(result.success).toBe(true);
    });
});

// DRY 적용: 공통 테스트 설정 추출
function setupDatabase() {
    const database = new Database();
    database.connect();
    return database;
}

function teardownDatabase(database) {
    database.disconnect();
}

describe('User service', () => {
    let database;
    
    beforeEach(() => {
        database = setupDatabase();
    });
    
    afterEach(() => {
        teardownDatabase(database);
    });
    
    it('should create a user', () => {
        const user = { name: 'John', email: 'john@example.com' };
        const result = createUser(database, user);
        expect(result.success).toBe(true);
    });
});

describe('Product service', () => {
    let database;
    
    beforeEach(() => {
        database = setupDatabase();
    });
    
    afterEach(() => {
        teardownDatabase(database);
    });
    
    it('should create a product', () => {
        const product = { name: 'Phone', price: 999 };
        const result = createProduct(database, product);
        expect(result.success).toBe(true);
    });
});

인프라 및 배포

인프라스트럭처 코드에서도 DRY 원칙을 적용할 수 있다. 인프라스트럭처 as 코드 (IaC) 도구를 사용하면 인프라 설정에서 중복을 줄일 수 있다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
# DRY하지 않은 Terraform 설정
resource "aws_instance" "web_server_1" {
  ami           = "ami-0c55b159cbfafe1f0"
  instance_type = "t2.micro"
  vpc_security_group_ids = ["sg-12345678"]
  subnet_id     = "subnet-12345678"
  tags = {
    Name = "WebServer1"
  }
}

resource "aws_instance" "web_server_2" {
  ami           = "ami-0c55b159cbfafe1f0"
  instance_type = "t2.micro"
  vpc_security_group_ids = ["sg-12345678"]
  subnet_id     = "subnet-12345678"
  tags = {
    Name = "WebServer2"
  }
}

# DRY 적용: 모듈 및 변수 사용
variable "ami_id" {
  default = "ami-0c55b159cbfafe1f0"
}

variable "instance_type" {
  default = "t2.micro"
}

variable "security_group_ids" {
  default = ["sg-12345678"]
}

variable "subnet_id" {
  default = "subnet-12345678"
}

module "web_server" {
  source = "./modules/web_server"
  count  = 2
  
  ami           = var.ami_id
  instance_type = var.instance_type
  vpc_security_group_ids = var.security_group_ids
  subnet_id     = var.subnet_id
  name          = "WebServer${count.index + 1}"
}

실제 사례 연구: DRY 원칙 적용

다음은 실제 애플리케이션에서 DRY 원칙을 적용하는 사례 연구:

사례 1: 코드 생성기

많은 프레임워크와 도구는 코드 생성을 사용하여 중복을 줄인다. 예를 들어, Ruby on Rails 의 스캐폴딩은 일반적인 CRUD 작업에 대한 코드를 자동 생성한다.

사례 2: 마이크로서비스의 공유 라이브러리

마이크로서비스 아키텍처에서는 인증, 로깅, 오류 처리 등의 공통 기능을 위한 공유 라이브러리를 만들어 서비스 간 코드 중복을 줄일 수 있다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// 공유 라이브러리: 인증 및 인가
package com.company.shared.auth;

public class AuthService {
    public static User authenticate(String token) {
        // 토큰 검증 및 사용자 정보 반환
    }
    
    public static boolean authorize(User user, String permission) {
        // 사용자 권한 확인
    }
}

// 마이크로서비스에서 사용
import com.company.shared.auth.AuthService;

@RestController
public class UserController {
    @GetMapping("/users/{id}")
    public User getUser(@RequestHeader("Authorization") String token, @PathVariable Long id) {
        User currentUser = AuthService.authenticate(token);
        if (!AuthService.authorize(currentUser, "READ_USER")) {
            throw new UnauthorizedException();
        }
        // 사용자 조회 로직
    }
}

사례 3: CSS 프레임워크

Bootstrap 과 같은 CSS 프레임워크는 공통 UI 패턴을 추상화하여 스타일 코드의 중복을 줄인다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
<!-- DRY하지 않은 CSS -->
<div style="margin: 20px; padding: 15px; border: 1px solid #ddd; border-radius: 5px;">
    <h2 style="font-size: 20px; margin-bottom: 10px;">제목</h2>
    <p style="line-height: 1.5;">내용…</p>
</div>

<!-- Bootstrap을 사용한 DRY 접근 방식 -->
<div class="card m-3">
    <div class="card-body">
        <h2 class="card-title">제목</h2>
        <p class="card-text">내용…</p>
    </div>
</div>

DRY 원칙의 진화와 현대적 적용

시간이 지남에 따라 DRY 원칙에 대한 이해와 적용은 진화해 왔다:

마이크로서비스 및 분산 시스템에서의 DRY

마이크로서비스 아키텍처에서는 서비스 간 중복이 때로는 허용된다. 각 서비스가 독립적으로 발전할 수 있도록 특정 유형의 중복이 허용되거나 심지어 권장되기도 한다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
// 서비스 A에서의 사용자 모델
public class User {
    private String id;
    private String name;
    private String email;
    // 서비스 A에 필요한 필드만 포함
}

// 서비스 B에서의 사용자 모델
public class User {
    private String id;
    private String name;
    private String address;
    // 서비스 B에 필요한 필드만 포함
}

이러한 중복은 서비스 간 결합을 줄이고 독립적인 배포를 가능하게 한다.

DRY 와 아키텍처 경계

DRY 는 아키텍처의 동일한 계층이나 경계 내에서 더 엄격하게 적용되어야 하지만, 다른 계층이나 경계를 가로지를 때는 중복을 더 관대하게 허용할 수 있다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
// 도메인 계층에서의 검증 로직
export class UserDomain {
    static validateEmail(email: string): boolean {
        return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
    }
}

// API 계층에서의 유사한 검증
// 아키텍처 경계를 가로지르는 일부 중복은 허용됨
export class UserController {
    validateUserInput(user: any): boolean {
        if (!user.email || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(user.email)) {
            return false;
        }
        // 기타 검증…
        return true;
    }
}

의도적 DRY 위반

때로는 성능, 안정성 또는 독립성을 위해 의도적으로 DRY 를 위반하는 것이 합리적일 수 있다.

1
2
3
4
5
6
7
8
// 성능을 위한 의도적 DRY 위반
public class PerformanceCriticalCode {
    public double calculateFast(double x) {
        // 외부 함수 호출 없이 직접 계산
        // 함수 호출 오버헤드를 피하기 위해 일부 로직 중복
        return x * x * 3.14159265359;
    }
}