공유된 기술노트 · 자바 · 1. 기초

newlec · 2026. 9. 23.

자바에서 "설정"이란 무엇인가: 선택권을 위한 코드 분리

1. 리플렉션 SQL 생성기의 한계

이전 편에서 리플렉션(Reflection)을 이용해 객체의 필드 이름을 자동으로 읽어와 SQL 문장을 생성하는 코드를 작성했습니다.

public class User {
    private int id;
    private String username;
    private String email;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

}

이 코드는 User 클래스의 필드(username, email)를 그대로 SQL의 컬럼명으로 사용하고, 클래스 이름을 테이블명으로 가정합니다.

문제점:

  • 만약 데이터베이스에서 컬럼명을 user_name으로 바꾸거나, 테이블명을 t_user로 변경해야 한다면?
  • 현재 코드는 클래스 소스 코드 자체를 수정해야만 SQL이 바뀝니다.
  • 배포된 애플리케이션의 소스를 수정하고 다시 컴파일하는 것은 비효율적입니다.

2. "설정"이란 무엇인가: 선택권을 주기 위한 데이터

"설정(Configuration)"이란, 사용자에게 선택권을 주기 위해 데이터를 분리하는 것입니다.

일상적 비유

  • 에어컨 리모컨: 에어컨 본체(코드)는 냉방/난방 기능을 가지고 있지만, 에어콘이 마음대로 온도를 설정할 수도 있지만 사용자가 원하는 온도(데이터)를 설정할 수 있도록 리모컨을 제공합니다. 본체를 뜯어내어 온도 조절 레버를 돌리는 것은 아닙니다.
  • 프린터: 프린터는 종이의 크기가 한 종류라고 생각하게 할 수도 있지만 용지 크기, 양면 인쇄 여부를 설정할 수 있게 해주면 사용자가 다양한 방식으로 프린터를 이용할 수 있습니다.

프로그래밍에서의 의미

  • 코드(Code): "무엇을 할 수 있는가?" (기능, 로직)
  • 설정(Configuration): "어떻게 할 것인가?" (값, 이름, 경로, 옵션)

리플렉션 SQL 생성기 같은 코드를 만들 때도 설정을 추가하면 사용자에게 선택권을 줄 수 있습니다. 예시로 돌아가보면:

  • 코드: 필드를 읽어와 SQL 문장 구조를 만드는 로직.
  • 설정: 코드만으로는 사용자가 끼어들 여분이 없습니다. 히지만 설정을 하게 되면 "이 클래스의 필드 username을 데이터베이스 컬럼으로 만들 때 user_name으로 바꿔서 쿼리를 만들어라”,라는 설정을 할 수가 있습니다.

이 두 가지를 분리하면, 코드를 바꾸지 않고 설정만 바꾸어 다른 데이터베이스 스키마에 대응할 수 있습니다.

3. 설정을 properties 파일로 저장하기

자바에서는 설정을 외부 파일로 분리하는 방법에는 XML 파일을 사용하거나**.properties** 파일을 사용할 수 있지만 가장 간단한 방법으로는 .properties를 사용하는 경우가 많습니다.

properties 파일 형식

  • key=value 형식으로 한 줄에 하나씩 작성합니다.
  • # 또는 !로 시작하는 줄은 주석입니다.
  • 값에 공백이 있으면 따옴표("")로 묶거나 백슬래시(\)로 이스케이프합니다.

db-config.properties 예시:

이 파일은 클래스 루트 main 함수가 있는 파일과 같은 경로에 있어야 합니다.

# 데이터베이스 테이블 이름
tableName=t_user

# 필드명 -> 컬럼명 매핑
columnName.username=user_name
columnName.age=user_age

Properties 클래스로 읽기

java.util.Properties 클래스는 이 파일을 읽어 keyvalue를 조회할 수 있게 해줍니다.

import java.io.FileInputStream;
import java.io.IOException;
import java.util.Properties;

public class ConfigLoader {
    public static Properties loadConfig(String filePath) throws IOException {
        Properties props = new Properties();
        try (FileInputStream fis = new FileInputStream(filePath)) {
            props.load(fis);
        }
        return props;
    }
}

4. SQL 생성기에 설정을 적용하기

이제 리플렉션 SQL 생성기를 수정하여, 하드코딩된 이름 대신 properties 파일에서 읽어온 설정을 사용하도록 변경합니다.

수정된 SqlGenerator

import java.lang.reflect.Field;
import java.util.Properties;

public class ConfigurableSqlGenerator {


    public String generateInsertSql(Object obj, Properties config) throws Exception {
        String className = obj.getClass().getSimpleName();
        
        // 1. 설정에서 테이블 이름을 가져옴 (없으면 클래스 이름 소문자로 기본값 사용)
        String tableName = config.getProperty("tableName", className.toLowerCase());
        
        StringBuilder sql = new StringBuilder();
        sql.append("INSERT INTO ").append(tableName).append(" (");
        
        Field[] fields = obj.getClass().getDeclaredFields();
        boolean first = true;
        
        for (Field field : fields) {
            if (java.lang.reflect.Modifier.isStatic(field.getModifiers())) continue;
            
            String fieldName = field.getName();
            
            // 2. 설정에서 컬럼 이름을 가져옴 (없으면 필드 이름 그대로 사용)
            // 키 형식: columnName.필드명
            String columnName = config.getProperty("columnName." + fieldName, fieldName);
            
            if (!first) {
                sql.append(", ");
            }
            sql.append(columnName);
            first = false;
        }
        
        sql.append(") VALUES (");
        for (int i = 0; i < fields.length; i++) {
            if (i > 0) sql.append(", ");
            sql.append("?");
        }
        sql.append(")");
        
        return sql.toString();
    }
}
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Properties;

public class App {
    public static void main(String[] args) {

        Properties config = new Properties();
        try (FileInputStream fis = new FileInputStream("db-config.properties")) {
            config.load(fis);
        } catch (IOException e) {
            System.err.println("설정 파일을 읽을 수 없습니다: " + e.getMessage());
            return;
        }
        
        // 2. User 객체 생성
        User user = new User();
        user.setId(1);
        user.setUsername("홍길동");
        user.setEmail("hong@newlecture.com");
        
        // 3. SQL 생성기 실행
        ConfigurableSqlGenerator generator = new ConfigurableSqlGenerator();
        try {
            String sql = generator.generateInsertSql(user, config);
            System.out.println("생성된 SQL: " + sql);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

실행 결과 비교

Case 1: 설정 파일 없음 (기본값 사용)

  • User 객체, username, email 필드
  • 결과: INSERT INTO user (username, email) VALUES (?, ?)

Case 2: db-config.properties 사용

  • tableName=t_user, columnName.username=user_name, columnName.email=user_email
  • 결과: INSERT INTO t_user (user_name, user_age) VALUES (?, ?)

핵심:

  • 같은 Java 코드 (ConfigurableSqlGenerator)가 실행되었습니다.
  • 설정 파일만 바뀌어서 다른 SQL 문장이 생성되었습니다.
  • 이것이 "사용자에게 선택권을 주는 것"입니다. 운영자는 코드를 건드리지 않고 properties 파일만 수정하면 됩니다.

5. 실제 프레임워크에서의 설정

이 원리는 실제 자바 프레임워크에서도 동일하게 적용됩니다.

  • 서블릿 (Servlet): web.xml (XML 형식) 또는 .properties 파일로 필터, 리스너, 초기 파라미터를 설정합니다.
  • 스프링 (Spring): application.yml 또는 application.properties 파일로 데이터베이스 URL, 포트, 빈(Bean)의 속성 값을 설정합니다.

요약: 어떤 프레임워크든, "코드는 고정되고, 값은 외부 설정 파일에서 읽어온다"는 구조를 따릅니다. properties 파일은 이 구조를 이해하는 가장 간단한 출발점입니다.

이 주소는 글의 저작자가 공개한 링크입니다. 뉴렉처 스터디의 다른 자료는 로그인 후 볼 수 있어요.