정보 |
---|
개발 가이드 |
목차 | ||||||
---|---|---|---|---|---|---|
|
1. 사용 라이브러리 정보
명칭 | 버전 | 용도 |
---|---|---|
JRE System Library | 1.8 | 웹 어플리케이션 구동에 필요한 자바 런타임 라이브러리 |
Spring Boot Framework | 5.3.4 | 서버 로직 전반을 담당하는 프레임워크 |
Gradle | 6.6 | 모듈의 의존성 관리를 위한 라이브러리 |
Mybatis-spring-boot-starter | 2.1.3 | 데이터베이스 연결 및 쿼리 처리를 위한 라이브러리 |
Logback | 로깅을 위한 라이브러리 |
2. 프로젝트 패키지 구조
1) src/main/java
프레임워크 공통 클래스 패키지: 예외처리, DB관련 속성, 보안, 유틸 클래스 포함
API 관련 비즈니스 로직 클래스 패키지: Controller, Service, Dao, Entity 클래스
프레임워크 관련 로직 클래스 패키지: RootController, User, Role 관련 설정 클래스
설정 관련 로직 클래스 패키지: 프로젝트 관련 설정 클래스
2) src/main/resources
프레임워크 설정 패캐지: dev/local 별 datasource 설정, 로깅 설정, 기타 설정 파일
API 관련 쿼리 패키지: 프로젝트 내에서 사용될 쿼리 파일
View 템플릿 패키지: Front-end html 파일과 error html 파일
기타 설정 패키지: 프로젝트 전체 appliation.yml 파일 및 설정 파일
3. 데이터베이스 연결
1) application.yml 파일 내 datasource 관련 설정 추가
아래와 같이 application.yml 파일에서 데이터베이스 관련 설정 파일 위치 명시
코드 블럭 | ||
---|---|---|
| ||
.... x2config: location: datasource: config/dev/properties/datasource.properties site: config/dev/properties/site.properties .... |
2) datasource.properties 파일 작성
src/main/resource/{}/properties 폴더 하위에 datasource.properties 파일 작성
prefix 값 설정 필요 (아래 예제에서는 x2base)
파일에 추가될 데이터베이스 관련 설정 정보
프로퍼티명
설명
url데이터베이스 접속 URLusername데이터베이스 사용자 아이디password데이터베이스 사용자 비밀번호driveClassName데이터베이스 드라이버 클래스 명
코드 블럭 | ||
---|---|---|
| ||
x2base.url=jdbc:log4jdbc:oracle:thin:@218.38.15.94:1521:ORAX2CO x2base.username=X2BASE x2base.password=ENC(cDiQFk2pHBC7aep3NKNDl4QteXJLwa5XOHfkm4oR+vEdo9SBz52xelxEio3snTwR) x2base.driver-class-name=net.sf.log4jdbc.sql.jdbcapi.DriverSpy |
3) DefaultDatasourceProperties.java 파일 작성
앞서 작성한 datasource.properties 파일 내 프로퍼티 값을 Java 파일에서 처리 가능하도록 다음과 같이 작업
@ConfigurationProperties 어노테이션을 이용하여 앞서 설정한 prefix 값으로 세팅하여 위의 설정 파일과 매핑
코드 블럭 | ||
---|---|---|
| ||
package com.plateer.base.config.properties; import javax.validation.constraints.NotNull; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.ConstructorBinding; import org.springframework.validation.annotation.Validated; import lombok.Getter; import lombok.ToString; /* location : /config/${spring.active.profile}/datasource.properties */ @ConfigurationProperties(prefix = "x2base") @ConstructorBinding @Validated @Getter @ToString public class DefaultDatasourceProperties { @NotNull private final String username; @NotNull private final String password; @NotNull private final String url; @NotNull private final String driverClassName; public DefaultDatasourceProperties(String username, String password, String url, String driverClassName) { this.username = username; this.password = password; this.url = url; this.driverClassName = driverClassName; } } |
4) 데이터베이스 관련 빈 설정 및 등록
앞서 설정한 파일 내 정보를 이용하여 SqlSessionFactory 및 SqlSessionTemplate 설정
@MapperScan 어노테이션으로 매퍼 파일 위치 및 SqlSessinoFactory 클래스 지정
트랜잭션 처리를 위하여 @EnableTransactionManagement 어노테이션 추가
코드 블럭 | ||
---|---|---|
| ||
package com.plateer.base.config.mybatis; import javax.sql.DataSource; import org.apache.ibatis.session.SqlSessionFactory; import org.mybatis.spring.SqlSessionFactoryBean; import org.mybatis.spring.SqlSessionTemplate; import org.mybatis.spring.annotation.MapperScan; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.jdbc.DataSourceBuilder; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; import org.springframework.jdbc.datasource.DataSourceTransactionManager; import org.springframework.transaction.annotation.EnableTransactionManagement; import com.plateer.base.config.properties.DefaultDatasourceProperties; import com.zaxxer.hikari.HikariDataSource; @Configuration @MapperScan(basePackages = "com.plateer.x2co.**.dao.**", sqlSessionFactoryRef = "sqlSessionFactory") @EnableTransactionManagement public class DefaultDatasourceConfig { private final String CLASSPATH_RESOURCES="classpath:mybatis/mapper/**/*.xml"; private final String CLASSPATH_MYBATIS_CONFIG="classpath:config/mybatis-config.xml"; @Autowired private ApplicationContext applicationContext; @Primary @Bean(name = "defaultDatasource") public DataSource defaultDatasource(DefaultDatasourceProperties datasourceProperties) { return DataSourceBuilder.create() .type(HikariDataSource.class) .driverClassName(datasourceProperties.getDriverClassName()) .url(datasourceProperties.getUrl()) .username(datasourceProperties.getUsername()) .password(datasourceProperties.getPassword()) .build(); } @Primary @Bean(name = "sqlSessionFactory") public SqlSessionFactory sqlSessionFactory(@Qualifier("defaultDatasource") DataSource defaultDatasource, ApplicationContext context) throws Exception { SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean(); sqlSessionFactoryBean.setDataSource(defaultDatasource); sqlSessionFactoryBean.setConfigLocation(applicationContext.getResource(CLASSPATH_MYBATIS_CONFIG)); sqlSessionFactoryBean.setMapperLocations(applicationContext.getResources(CLASSPATH_RESOURCES)); return sqlSessionFactoryBean.getObject(); } @Primary @Bean(name = "sqlSessionTemplate") public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) { return new SqlSessionTemplate(sqlSessionFactory); } @Primary @Bean(name="transactionManager") public DataSourceTransactionManager transactionManager(@Autowired @Qualifier("defaultDatasource") DataSource defaultDatasource) { return new DataSourceTransactionManager(defaultDatasource); } } |
4. 다중 Datasource 연결
1) 데이터베이스 관련 정보 설정
datasource.properties 파일 내 DB 접속 정보 각각 설정
코드 블럭 | ||
---|---|---|
| ||
spring.primary.datasource.jdbc-url=jdbc:oracle:thin:@218.38.15.94:1521:ORAX2CO spring.primary.datasource.username=X2BASE spring.primary.datasource.password=ENC(cDiQFk2pHBC7aep3NKNDl4QteXJLwa5XOHfkm4oR+vEdo9SBz52xelxEio3snTwR) spring.secondary.datasource.jdbc-url=jdbc:oracle:thin:@218.38.15.94:1521:ORAX2CO spring.secondary.datasource.username=X2BASE spring.secondary.datasource.password=ENC(cDiQFk2pHBC7aep3NKNDl4QteXJLwa5XOHfkm4oR+vEdo9SBz52xelxEio3snTwR) |
2) Datasource 설정
다중 Datasource 설정 시, 각각 Datasource 클래스 생성
@ConfigurationProperties 어노테이션으로 datasource.properties 내 DB 접속 정보 로드
펼치기 | ||||
---|---|---|---|---|
|
|
코드 블럭 | ||
---|---|---|
| ||
package com.plateer.base.config.mybatis;
import javax.sql.DataSource;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
@Configuration
@MapperScan(basePackages = "com.plateer.x2co.**.dao.**", sqlSessionFactoryRef = "sqlSessionFactory") // Mapper 파일 스캔을 위한 패키지 기입
@EnableTransactionManagement
public class PrimaryDatasource {
private final String CLASSPATH_RESOURCES="classpath:mybatis/mapper/**/*.xml"; // Mapper 파일 스캔을 위한 경로 정보 기입
private final String CLASSPATH_MYBATIS_CONFIG="classpath:config/mybatis-config.xml"; // mybatis 관련 설정 파일 경로 정보 기입
@Primary
@Bean(name="dataSource")
@ConfigurationProperties(prefix="spring.primary.datasource")
public DataSource dataSource() {
return DataSourceBuilder.create().build();
}
@Primary
@Bean(name="sqlSessionFactory")
public SqlSessionFactory sqlSessionFactoryBean(@Autowired @Qualifier("dataSource") DataSource dataSource, ApplicationContext applicationContext)
throws Exception {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dataSource);
sqlSessionFactoryBean.setConfigLocation(applicationContext.getResource(CLASSPATH_MYBATIS_CONFIG));
sqlSessionFactoryBean.setMapperLocations(applicationContext.getResources(CLASSPATH_RESOURCES));
return sqlSessionFactoryBean.getObject();
}
@Primary
@Bean(name="sqlSession")
public SqlSessionTemplate sqlSession(@Autowired @Qualifier("sqlSessionFactory") SqlSessionFactory sqlSessionFactory) {
return new SqlSessionTemplate(sqlSessionFactory);
}
@Primary
@Bean(name="transactionManager")
public DataSourceTransactionManager transactionManager(@Autowired @Qualifier("dataSource") DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
}
|
코드 블럭 | ||
---|---|---|
| ||
package com.plateer.base.config.mybatis;
import javax.sql.DataSource;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
@Configuration
@MapperScan(basePackages = "com.plateer.x2co.**.dao.**", sqlSessionFactoryRef = "secondarySqlSessionFactory")
@EnableTransactionManagement
public class SecondaryDatasource {
private final String CLASSPATH_RESOURCES="classpath:mybatis/mapper/**/*.xml";
private final String CLASSPATH_MYBATIS_CONFIG="classpath:config/mybatis-config.xml";
@Bean(name="secondaryDataSource")
@ConfigurationProperties(prefix="spring.secondary.datasource")
public DataSource secondaryDataSource() {
return DataSourceBuilder.create().build();
}
@Bean(name="secondarySqlSessionFactory")
public SqlSessionFactory secondarySqlSessionFactoryBean(@Autowired @Qualifier("secondaryDataSource") DataSource secondaryDataSource, ApplicationContext applicationContext)
throws Exception {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(secondaryDataSource);
sqlSessionFactoryBean.setConfigLocation(applicationContext.getResource(CLASSPATH_MYBATIS_CONFIG));
sqlSessionFactoryBean.setMapperLocations(applicationContext.getResources(CLASSPATH_RESOURCES));
return sqlSessionFactoryBean.getObject();
}
@Bean(name="secondarySqlSession")
public SqlSession secondarySqlSession(@Autowired @Qualifier("secondarySqlSessionFactory") SqlSessionFactory secondarySqlSessionFactory) {
return new SqlSessionTemplate(secondarySqlSessionFactory);
}
@Bean(name="secondaryTransactionManager")
public DataSourceTransactionManager secondaryTransactionManager(@Autowired @Qualifier("secondaryDataSource") DataSource secondaryDataSource) {
return new DataSourceTransactionManager(secondaryDataSource);
}
} |
|
3) 사용 예
|
5. 로깅 설정
1) 로깅 관련 프로퍼티 설정
로깅 파일 저장 위치 설정
로깅 레벨 설정
라이트 로깅 여부 설정 (최소한의 정보만 로깅)
로그에 컨트롤러 파라미터를 포함할지 여부 설정
로그에 서비스 파라미터를 포함할지 여부 설정
|
2) logback 관련 설정
src/main/resources/{} 폴더 하위 loback.xml 파일 설정
consoleAppender: 시스템 콘솔에 찍히는 로그 정보
fileAppender: 파일에 쓰여질 로그 정보
asyncConsoleAppender: 로깅 작업이 성능에 많은 영향을 주는 경우, 비동기로 설정하여 로깅 부하를 최소화 (콘솔 로깅 비동기 처리 설정)
asyncFileAppender: 로깅 작업이 성능에 많은 영향을 주는 경우, 비동기로 설정하여 로깅 부하를 최소화 (파일 로깅 비동기 처리 설정)
|
3) 로깅 추가 정보 설정
로깅에 추가로 보여주고자 하는 정보 설정 (src/main/java/com/plateer/base/log 폴더 하위 ExecLoggingFilter.java 파일 수정)
아래 가이드는 RequestID 와 SessionID 를 설정하는 예제
|
Request 시 ReqeustID, SessionID 를 생성하고 제거하며, MDC 를 활용하여 로그 출력
6. 쿼리 로깅 설정
log4jdbc 를 이용한 쿼리 로깅 방법
1) log4jdbc 설정 파일 추가
resource 폴더 하위 아래 파일 추가
|
2) 데이터베이스 연결 정보 수정
driver-class-name=net.sf.log4jdbc.sql.jdbcapi.DriverSpy 추가
url 수정 (log4jdbc 추가)
|
3) logback.xml 파일 수정
아래 설정 내용 추가
|
7. 클라이언트, 서버 간 통신 데이터 형식
Map 형태가 아닌 VO(Value Object) 로 데이터 통신하는 것을 기본으로 함
아래 사용자 정보 처리 관련 VO 예제 참고
|
8. 서버 측 개발 방식
Controller, Service, Mapper 구조
서버는 Spring MVC 패턴 기반이므로 데이터 처리를 위하여 다음과 같이 Controller, Service, Mapper 구조로 진행됨
http request → (mapping) → Controller → Service → ServiceImpl → Mapper → query 호출 순서로 진행됨
1) VO 클래스 작성
src/main/java 하위 해당 업무 폴더에 entity 폴더 생성 후 아래와 같이 작업 진행
데이터 전달을 위하여 사용될 객체 사전 정의
|
2) Controller 클래스 작성
src/main/java 하위 해당 업무 폴더에 Controller 폴더 생성 후 아래와 같이 작업 진행
@RestController 어노테이션으로 Controller 임을 명시
@RequestMapping 어노테이션으로 해당 Controller 전체에 대한 Http Request 매핑 정보 명시
@Autowired 어노테이션으로 service 인터페이스 주입
|
3) Service 클래스 작성
src/main/java 하위 해당 업무 폴더에 Service 폴더 생성 후 아래와 같이 작업 진행
구현하고자 하는 업무 로직의 인터페이스 작성
|
위 인터페이스의 실제 구현체 작성
@Service 어노테이션으로 서비스 클래스 임을 명시
@Autowired 어노테이션으로 관련 Mapper 클래스 주입
|
4) Mapper 클래스 및 Query 작성
src/main/java 하위 해당 업무 폴더에 dao 폴더 생성 후 아래와 같이 작업 진행
@Mapper 어노테이션을 이용하여 Mapper 임을 명시
|
src/main/resource 하위에 해당 쿼리 작성
namespace 에 위 Mapper 클래스 명시
resultType 혹은 parameterType 에 데이터 객체 명시
|
위 작업 진행 시 최종 패키지 구조는 다음과 같음
9. 유효성 체크 방법
1) Bean Validation
데이터 통신을 VO 로 하므로, 다음과 같이 VO 객체에 javax.validation.constrains 패키지에서 제공하는 어노테이션을 이용
|
1-1) @Valid 를 이용한 @RequestBody 에 대한 유효성 검증
|
1-2) @Validated 를 이용한 @PathVariable과 @RequestParam 에 대한 유효성 검증
|
2) Custom Validator
javax.validation.constrains 패키지에서 제공되지 않는 validator 를 구현하고자 하는 경우, 본 방법으로 진행
2-1) Custom annotation 생성
|
2-2) Custom annotation 을 처리할 Custom Validator 생성
|
2-3) Custom annotation @LocaleConstraint을 적용하여 유효성 검증
|
10. 에러메시지 및 예외 처리
1) 에러 메시지 처리
X2Commerce 4.0 에서 사용될 에러 메시지 포맷
|
오류 발생 시 아래와 같이 응답 (HTTP Response code 는 헤더에 포함되어 있기 때문에 추가로 전송하지 않음)
|
2) 예외 처리 기능 (com.plateer.base.exception 패키지 하위 X2ExceptionController 클래스 구현)
ResponseEntityExceptionHandler 확장 구현
Spring 에서 기본적으로 제공해주는 ResponseEntityExceptionHandler 를 상속 받아 Exception Handler 구현
@ControllerAdvice 어노테이션을 이용하여 Exception 을 일괄 처리
|