정보 |
---|
개발 가이드 |
목차 | ||||||
---|---|---|---|---|---|---|
|
1. 사용 라이브러리 정보
명칭 | 버전 | 용도 |
---|---|---|
JRE System Library | 1.8 | 웹 어플리케이션 구동에 필요한 자바 런타임 라이브러리 |
Spring Boot Framework | 5.3.9 | 서버 로직 전반을 담당하는 프레임워크 |
Mybatis-spring-boot-starter | 2.5.3 | 데이터베이스 연결 및 쿼리 처리를 위한 라이브러리 |
Logback | 로깅을 위한 라이브러리 | |
thymeleaf-spring | 3.0.12 | 컨트롤러가 전달하는 데이터를 VIEW에 표시해주는 라이브러리 |
lombok | 1.18.20 | 반복되는 메소드를 Annotation을 사용해서 자동으로 작성해주는 라이브러리 |
lucy-xss-servlet | 2.0.1 | XSS 방지 필터 |
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 파일 내 port 및 DB 관련 설정 추가
아래와 같이 application.yml 파일에서 서버 port번호와 데이터베이스 관련 설정 명시
단일연결
|
다중연결
|
프로퍼티명 | 설명 |
---|---|
port | tomcat 포트 |
url | 데이터베이스 접속 URL |
username | 데이터베이스 사용자 아이디 |
password | 데이터베이스 사용자 비밀번호 |
driveClassName | 데이터베이스 드라이버 클래스 명 |
hikari | 기타 |
session | 스프링 session 설정 |
zipkin | MSA 환경에서 분산 트렌젝션의 추적 |
2) *******DatabaseConfig.java 파일 작성
DisplayRodbDatabaseConfig.java
|
3) mybatis-config.xml 파일 작성
데이터베이스 데이터 마스킹, 암호화 관련 java 파일 연결
|
4) 데이터베이스 데이터 마스킹, 암호화 관련 java파일 작성
MybatisMaskingInterceptor.java
|
MybatisEncryptInterceptor.java
|
5. 로깅 설정
1)
로깅 관련 프로퍼티 설정로깅 파일 저장 위치 설정
로깅 레벨 설정
라이트 로깅 여부 설정 (최소한의 정보만 로깅)
로그에 컨트롤러 파라미터를 포함할지 여부 설정
로그에 서비스 파라미터를 포함할지 여부 설정
쿼리로깅 log4jdbc 설정 파일 추가
resource 폴더 하위 아래 파일 추가
|
2) logback 관련 설정
src/main/resources/{} 폴더 하위 loback-spring.xml 파일 설정
consoleAppender: 시스템 콘솔에 찍히는 로그 정보
fileAppender: 파일에 쓰여질 로그 정보
asyncConsoleAppender: 로깅 작업이 성능에 많은 영향을 주는 경우, 비동기로 설정하여 로깅 부하를 최소화 (콘솔 로깅 비동기 처리 설정)
asyncFileAppender: 로깅 작업이 성능에 많은 영향을 주는 경우, 비동기로 설정하여 로깅 부하를 최소화 (파일 로깅 비동기 처리 설정)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3) 로깅 추가 정보 설정
로깅에 추가로 보여주고자 하는 정보 설정 (src/main/java/com/plateer/base/log 폴더 하위 ExecLoggingFilter.java 파일 수정)
아래 가이드는 RequestID 와 SessionID 를 설정하는 예제
코드 블럭 |
---|
package com.plateer.base.filter; import java.io.IOException; import java.util.UUID; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import org.slf4j.MDC; import org.slf4j.MDC.MDCCloseable; import org.springframework.stereotype.Component; import lombok.extern.slf4j.Slf4j; @Slf4j @Component public class ExecLoggingFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void destroy() { } @Override public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { try { HttpServletRequest request = (HttpServletRequest) req; // request id setting MDC.put("rid", getRequestId(request)); // session id setting MDC.put("sid", getSessionId(request)); chain.doFilter(req, res); } finally { // mdc remove MDC.clear(); } } private String getRequestId(HttpServletRequest request) { return request.getRequestURI() + "-" + UUID.randomUUID().toString(); } private String getSessionId(HttpServletRequest request) { String sessionId = request.getRequestedSessionId(); return sessionId != null ? request.getRequestedSessionId() : "?????"; } } |
Request 시 ReqeustID, SessionID 를 생성하고 제거하며, MDC 를 활용하여 로그 출력
6. 쿼리 로깅 설정
log4jdbc 를 이용한 쿼리 로깅 방법
1) log4jdbc 설정 파일 추가
resource 폴더 하위 아래 파일 추가
코드 블럭 |
---|
log4jdbc.spylogdelegator.name=net.sf.log4jdbc.log.slf4j.Slf4jSpyLogDelegator
log4jdbc.dump.sql.maxlinelength=0 |
2) 데이터베이스 연결 정보 수정
driver-class-name=net.sf.log4jdbc.sql.jdbcapi.DriverSpy 추가
url 수정 (log4jdbc 추가)
코드 블럭 |
---|
x2base.driver-class-name=net.sf.log4jdbc.sql.jdbcapi.DriverSpy
x2base.url=jdbc:log4jdbc:oracle:thin:@218.38.15.94:1521:ORAX2CO
x2base.username=X2BASE
x2base.password=ENC(cDiQFk2pHBC7aep3NKNDl4QteXJLwa5XOHfkm4oR+vEdo9SBz52xelxEio3snTwR) |
3) logback.xml 파일 수정
아래 설정 내용 추가
코드 블럭 |
---|
<!-- log4jdbc 옵션 설정 -->
<logger name="jdbc" level="OFF"/>
<!-- 커넥션 open close 이벤트 로그로 남김 -->
<logger name="jdbc.connection" level="OFF"/>
<!-- SQL문만을 로그로 남기며, PreparedStatement일 경우 관련된 argument 값으로 대체된 SQL문이 보여짐 -->
<logger name="jdbc.sqlonly" level="OFF"/>
<!-- SQL문과 해당 SQL을 실행시키는데 수행된 시간 정보(milliseconds)를 포함 -->
<logger name="jdbc.sqltiming" level="DEBUG"/>
<!-- ResultSet을 제외한 모든 JDBC 호출 정보를 로그로 남김. 방대한 양의 로그가 생성되므로 특별히 JDBC 문제를 추적해야 할 필요가 있는 경우를 제외하고는 사용을 권장하지 않음-->
<logger name="jdbc.audit" level="OFF"/>
<!-- ResultSet을 포함한 모든 JDBC 호출 정보를 로그로 남기므로 방대한 양의 로그가 생성됨 -->
<logger name="jdbc.resultset" level="OFF"/>
<!-- SQL 결과 조회된 데이터의 table을 로그로 남김 -->
<logger name="jdbc.resultsettable" level="DEBUG"/> |
7. 클라이언트, 서버 간 통신 데이터 형식
Map 형태가 아닌 VO(Value Object) 로 데이터 통신하는 것을 기본으로 함
아래 사용자 정보 처리 관련 VO 예제 참고
코드 블럭 |
---|
package com.plateer.x2co.common.entity;
import java.util.Date;
import lombok.Data;
@Data
public class User {
private String usrId;
private String usrNm;
private String usrGrp;
private String useYn;
private String mobileNo;
private String phoneNo;
private String email;
private String pw;
private Date pwModDt;
private String mailReceivedYn;
private String smsReceivedYn;
private String pwMissCnt;
private String pwInitYn;
private Date lastLoginDt;
private String lastLoginIp;
private String accExpireYn;
private String accLockYn;
private String pwModRequireYn;
} |
8. 서버 측 개발 방식
Controller, Service, Mapper 구조
서버는 Spring MVC 패턴 기반이므로 데이터 처리를 위하여 다음과 같이 Controller, Service, Mapper 구조로 진행됨
http request → (mapping) → Controller → Service → ServiceImpl → Mapper → query 호출 순서로 진행됨
1) VO 클래스 작성
src/main/java 하위 해당 업무 폴더에 entity 폴더 생성 후 아래와 같이 작업 진행
데이터 전달을 위하여 사용될 객체 사전 정의
코드 블럭 |
---|
package com.plateer.x2co.backoffice.sample.entity;
import lombok.Data;
@Data
public class SampleVO{
String itemNo;
String salePrc;
String mbrNo;
....
} |
2) Controller 클래스 작성
src/main/java 하위 해당 업무 폴더에 Controller 폴더 생성 후 아래와 같이 작업 진행
@RestController 어노테이션으로 Controller 임을 명시
@RequestMapping 어노테이션으로 해당 Controller 전체에 대한 Http Request 매핑 정보 명시
@Autowired 어노테이션으로 service 인터페이스 주입
코드 블럭 |
---|
package com.plateer.x2co.backoffice.sample.controller;
@RestController
@RequestMapping("/rest")
public class SampleController {
@Autowired
private SampleService sampleService ;
@PostMapping(value="/categories", consumes = MediaType.APPLICATION_JSON_UTF8_VALUE, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public ReponseEntity<List<SampleVO>> getPrDispCatList(@RequestBody String id) {
List<SampleVO> listMap = prototypeServcie.getPrDispCatList(id);
return sampleService.getPrDispCatList(id);
}
} |
3) Service 클래스 작성
src/main/java 하위 해당 업무 폴더에 Service 폴더 생성 후 아래와 같이 작업 진행
구현하고자 하는 업무 로직의 인터페이스 작성
코드 블럭 |
---|
package com.plateer.x2co.backoffice.sample.service;
public interface SampleService {
public ReponseEntity<List<SampleVO>> getPrDispCatList(String id);
} |
위 인터페이스의 실제 구현체 작성
@Service 어노테이션으로 서비스 클래스 임을 명시
@Autowired 어노테이션으로 관련 Mapper 클래스 주입
코드 블럭 |
---|
package com.plateer.x2co.backoffice.sample.service;
@Service
public class SampleServiceImpl implements SampleService {
@Autowired
private SampleMapper sampleMapper ;
@Override
public ReponseEntity<List<SampleVO>> getPrDispCatList(String id) {
return ResponseEntity.ok(sampleMapper.getPrDispCatList(id));
}
} |
4) Mapper 클래스 및 Query 작성
src/main/java 하위 해당 업무 폴더에 dao 폴더 생성 후 아래와 같이 작업 진행
@Mapper 어노테이션을 이용하여 Mapper 임을 명시
코드 블럭 |
---|
package com.plateer.x2co.backoffice.sample.dao;
@Mapper
public interface SampleMapper {
public List<SampleVO> getPrDispCatList(String id);
} |
src/main/resource 하위에 해당 쿼리 작성
namespace 에 위 Mapper 클래스 명시
resultType 혹은 parameterType 에 데이터 객체 명시
코드 블럭 |
---|
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.plateer.x2co.api.backoffice.sample.dao.SampleMapper">
<!-- 카테고리 계층 목록 조회 -->
<select id="getPrDispCatList" resultType="com.plateer.x2co.backoffice.sample.entity.SampleVO">
/* prDispCatBase.getPrDispCatList */
SELECT
....
</select> |
위 작업 진행 시 최종 패키지 구조는 다음과 같음
9. 유효성 체크 방법
1) Bean Validation
데이터 통신을 VO 로 하므로, 다음과 같이 VO 객체에 javax.validation.constrains 패키지에서 제공하는 어노테이션을 이용
|
6. 클라이언트, 서버 간 통신 데이터 형식
Map 형태가 아닌 VO(Value Object) 로 데이터 통신하는 것을 기본으로 함
아래 사용자 정보 처리 관련 VO 예제 참고
User.java
|
7. 서버 측 개발 방식
Controller, Service, (APIController, service), Mapper 구조
서버는 Spring MVC 패턴 기반이므로 데이터 처리를 위하여 다음과 같이 Controller, Service, Mapper 구조로 진행됨
MAS 구조로 BO 에서 API서버를 호출하여 DB서버와 통신하는 형태
http request → (mapping) → Controller → Service → ServiceImpl → APIController → Service → ServiceImpl → Mapper → XML(query) 호출 순서로 진행됨
1) DTO 클래스 작성
src/main/java 하위 해당 업무 폴더에 entity 폴더 생성 후 아래와 같이 작업 진행
데이터 전달을 위하여 사용될 객체 사전 정의
Sample.java
|
2) Controller 클래스 작성
src/main/java 하위 해당 업무 폴더에 Controller 폴더 생성 후 아래와 같이 작업 진행
@RestController 어노테이션으로 Controller 임을 명시
@RequestMapping 어노테이션으로 해당 Controller 전체에 대한 Http Request 매핑 정보 명시
@RequiredArgsConstructor어노테이션으로 생성자 명시
SampleController.java
|
3) Service 클래스 작성
src/main/java 하위 해당 업무 폴더에 Service 폴더 생성 후 아래와 같이 작업 진행
구현하고자 하는 업무 로직의 인터페이스 작성
SampleService.java
|
위 인터페이스의 실제 구현체 작성
@Service 어노테이션으로 서비스 클래스 임을 명시
@Value 어노테이션으로 API 서버 URL 명시
RestApiUtil 의 get, post, put, delete 메서드를 이용하여 API 호출
SampleServiceImpl.java
|
4) API Controller 클래스 작성
API 서버의 Controller 작성
Response 객체로 Return
SampleController.java
|
5) API Service 클래스 작성
SampleService.java
|
위 인터페이스의 실제 구현체 작성
SampleServiceImpl.java
|
6) Mapper 클래스 및 Query 작성
src/main/java 하위 해당 업무 폴더에 repository/{db연결명} 폴더 생성 후 아래와 같이 작업 진행
SampleMapper.java
|
src/main/resource 하위에 해당 쿼리 작성
namespace 에 위 Mapper 클래스 명시
resultType 혹은 parameterType 에 데이터 객체 명시
SampleMapper.xml
|
위 작업 진행 시 최종 패키지 구조는 다음과 같음
8. 유효성 체크 방법
1) Bean Validation
Alias 어노테이션으로 별칭 지정
데이터 통신을 VO 로 하므로, 다음과 같이 VO 객체에 javax.validation.constrains 패키지에서 제공하는 어노테이션을 이용
Group.java
|
1-1) @Valid 를 이용한 @RequestBody 에 대한 유효성 검증
SampleController.java
|
1-2) @Validated 를 이용한 @PathVariable과 @RequestParam 에 대한 유효성 검증
SampleController.java
|
2) Custom Validator
javax.validation.constrains 패키지에서 제공되지 않는 validator 를 구현하고자 하는 경우, 본 방법으로 진행
2-1) Custom annotation 생성
LocaleConstraint.java
|
2-2) Custom annotation 을 처리할 Custom Validator 생성
LocaleValidator.java
|
2-3) Custom annotation @LocaleConstraint을 적용하여 유효성 검증
Category.java
|
|
|
|
|
|
|
|
|
|
|
1-1) @Valid 를 이용한 @RequestBody 에 대한 유효성 검증
|
|
|
|
|
1-2) @Validated 를 이용한 @PathVariable과 @RequestParam 에 대한 유효성 검증
코드 블럭 |
---|
@RestController
@Validated
public class SampleController{
@GetMapping("/users/{email}")
public String getUserInfoByEmail(@PathVariable("email") @Email String email) {
....
}
} |
2) Custom Validator
javax.validation.constrains 패키지에서 제공되지 않는 validator 를 구현하고자 하는 경우, 본 방법으로 진행
2-1) Custom annotation 생성
코드 블럭 |
---|
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import javax.validation.Constraint;
import javax.validation.Payload;
@Target({FIELD})
@Retention(RUNTIME)
@Constraint(validatedBy = LocaleValidator.class)
@Documented
public @interface LocaleConstraint {
String message() default "Invalid Locale";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
|
2-2) Custom annotation 을 처리할 Custom Validator 생성
|
9. 에러메시지 및 예외 처리
1) 에러 메시지 처리
X2BEE 1.0 에서 사용할 에러처리 포멧'
GlobalErrorController.java
|
|
|
|
|
|
|
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 을 일괄 처리
|
API 오류 처리
Response 객체에 Error 내용을 담아 Return
ErrorResponse.java
|
Error Code 정의
|
2) 예외 처리 기능 (x2bee-common 패키지 하위 Exception 클래스 구현)
src/main/java 아래에 패키지 생성 후 Exception 코드 작성
예시1
CommonException.java
|
예시2
ValidationException.java
|
10. Transaction 처리
등록/수정/삭제 서비스 메소드에 자동으로 트랜잭션이 동작하도록 AOP 가 설정되어있음
register/modify/delete/save 를 메소드 이름에 포함시켜야 동작하므로 메소드 명칭에 오타가 없도록 유의
registerSampleMulti.java
|
DisplayDbTranscationAspect.java
|