공유된 기술노트 · 자바 · 1. 기초
자바에서 "설정"이란 무엇인가: 선택권을 위한 코드 분리
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_ageProperties 클래스로 읽기
java.util.Properties 클래스는 이 파일을 읽어 key로 value를 조회할 수 있게 해줍니다.
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 파일은 이 구조를 이해하는 가장 간단한 출발점입니다.
이 주소는 글의 저작자가 공개한 링크입니다. 뉴렉처 스터디의 다른 자료는 로그인 후 볼 수 있어요.