API 개발 가이드
x2bee-api 프로젝트는 마이크로 서비스별 api를 제공하는 프로젝트입니다.
각 마이크로 서비스별로 구분되어 x2bee-api-display(전시), x2bee-api-order(주문) 등등의 프로젝트로 나뉘어져 있습니다.
x2bee-api의 모든 API는 Rest API로 제공되며, Json 형식으로 입력 값과 출력 값을 처리합니다.
x2bee-api에는 세션 정보와 같은 상태정보가 없습니다.
API 처리에 필요한 정보는 그때 그때 입력값으로 받거나 DB조회해서 얻어야 하며, 불가피한 경우 캐시를 사용할 수도 있습니다.
x2bee-api는 클라이언트에서 호출되거나, 다른 x2bee-api에서 호출될 수 있습니다.
패키지명
업무 대분류를 기준으로 패키지를 분류하고 명명합니다.
ex) 샘플 패키지/폴더
구분 | 패키지명/폴더명 |
컨트롤러 | com.x2bee.api.display.app.controller.sample |
서비스 | com.x2bee.api.display.app.service.sample |
리포지토리 | com.x2bee.api.display.app.repository.sample |
DTO | com.x2bee.api.display.app.dto.request.sample com.x2bee.api.display.app.dto.response.sample |
mapper XML | mapper/rwdb/sample mapper/rodb/display |
message(다국어처리) | message/display |
컨트롤러 작성
컨트롤러는 입력 파라미터를 받아서 DTO에 설정하고, 업무로직 처리를 위한 서비스 메소드를 호출한 후 서비스 메소드의 결과값을 적절한 응답 식으로 변화하여 return합니다.
클래스 어노테이션
클래스 레벨에 다음의 어노테이션을 사용합니다.
종류 | 어노테이션 | 설명 |
Spring | @RestController | Rest컨트롤러임을 표시하는 springframework bean 어노테이션. @ResponseBody + @Controller 두가지 역할을 합니다. |
@RequestMapping | 컨트롤러 클래스 수준의 Request Mapping URI 공통부분을 지정합니다. | |
@RequiredArgsConstructor | 생성자 주입 편의를 위한 lombok 어노테이션 입니다. | |
@Slf4j | 로그 작성을 위해서 사용하는 lombok 어노테이션 입니다. | |
Swagger3 UI | @Tag | 해당 어노테이션은 Swagger api 그룹 설정시 사용합니다. |
@RestController
@RequestMapping("/categories")
@Slf4j
@RequiredArgsConstructor
@Tag(name = "카테고리 관리 Controller", description = "카테고리 API")
public class CategoryController {
...
}
메소드 어노테이션
메소드 레벨에 다음의 어노테이션을 사용합니다.
종류 | 어노테이션 | 설명 |
HTTP 요청 | @GetMapping | 요청 URI와 컨트롤러 메소드와의 매핑을 지정한다. get 메소드(조회/검색) 시 사용한다. |
@PostMapping | 요청 URI와 컨트롤러 메소드와의 매핑을 지정한다. post 메소드(등록) 시 사용한다. | |
@PutMapping | 요청 URI와 컨트롤러 메소드와의 매핑을 지정한다. put 메소드(전체수정) 시 사용한다. | |
@PatchMapping | 요청 URI와 컨트롤러 메소드와의 매핑을 지정한다. patch 메소드(일부수정) 시 사용한다. | |
@DeleteMapping | 요청 URI와 컨트롤러 메소드와의 매핑을 지정한다. delete 메소드(삭제) 시 사용한다. | |
Swagger3 UI | @Operation | 해당 어노테이션은 Swagger api 그룹 설정시 사용한다. |
@ApiResponse | 해당 어노테이션은 Swagger api response 설정시 사용한다. | |
@Parameters | 해당 어노테이션은 Swagger api parameter 그룹 설정시 사용한다. | |
@Parameter | 해당 어노테이션은 Swagger api parameter 설정시 사용한다. |
@Operation(summary = "카테고리 목록 조회", description = "해당 카테고리 목록을 조회한다")
@Parameters({
@Parameter(name = "siteNo", description = "사이트번호 (x2bee.com : 1)", required = true, example = "1"),
@Parameter(name = "useYn", description = "사용여부 (사용함: Y, 사용안함: N)", required = true, example = "Y")
})
@ApiResponses (
value = {
@ApiResponse(responseCode = "200", description = "몰 정보 조회 성공", content = @Content(schema = @Schema(implementation = Category.class))),
@ApiResponse(responseCode = "400", description = "몰 정보 조회 실패", content = @Content(schema = @Schema(implementation = ErrorCode.class)))
}
)
@GetMapping(value="/trees")
public List<Category> getCategoryTreeList(PrDispCtgBaseRequest prDispCtgBaseRequest) throws Exception {
...
return categoryTreeList;
}
매핑 URI 형식
@RequestMapping의 path파라미터로 작성될 매핑 URI의 형식은 다음과 같습니다.
/api/<업무대분류명(패키지명)>/resource명 복수형/하위resource명 복수형 |
/api/<업무대분류명(패키지명)>: context-path로 지정. 프로그램에서 지정하지 않음.
/resource명 복수형: 클래스레벨 @RequestMapping에서 지정.
/하위resource명 복수형: 메소드레벨 Mapping에서 지정. 없는 경우 생략함.
ex) 카테고리 처리:클래스레벨 RequestMapping: @RequestMapping("/api/display/categories")
기능 | Method레벨 RequestMapping |
카테고리 트리 조회 | @GetMapping("/trees") |
카테고리 상세 조회 | @GetMapping("/{id}") |
카테고리 등록 | @PostMapping("") |
카테고리 수정 | @PutMapping("/{id}") |
카테고리 삭제 | @DeleteMapping("/{id}") |
카테고리 비전시 처리 | @PatchMapping("/{id}?displayYn=false") |
메소드 파라미터 어노테이션
메소드 파라미터에 다음의 어노테이션을 사용할 수 있습니다.
어노테이션 | 설명 |
@RequestBody | 모든 API 파라미터 전달은 Request body에 Json 유형의 데이터를 사용합니다. 해당 파라미터를 입력받기 위해서는 파라미터 선언에 @RequestBody 어노테이션을 사용합니다. |
@Valid, @Validated | 파라미터 모델클래스 맴버변수의 유효성을 체크합니다. 맴버변수에 @NotNull, @Size, @Min, @Max, @Digits, @Pattern 등의 제약사항을 주고 검증실패 시 MethodArgumentNotValidException 이 발생합니다. |
public Response<String> savePrDispGoodsInfo(@RequestBody @Valid
PrDispGoodsInfo prDispGoodsInfo) throws Exception {
...
}
메소드 리턴값
API응답은 응답데이터를 Response 객체로 감싸서 return합니다.
클래스 레벨에 @RestController 어노테이션이 사용되었으므로 실제 응답값은 java 객체를 JSON으로 변환한 값이 됩니다.
다음은 Response 클래스 소스입니다.
Response.java
응답값이 작성된 Timestamp가 전달되며, 처리 오류코드와 메세지를 지정할 수 있습니다.
payload에는 실제 응답 데이터가 들어갑니다.
서비스 클래스 작성
서비스 클래스는 A의 업무로직을 처리를 담당합니다.
인터페이스/구현클래스 구분
소메뉴별로 1개의 서비스 클래스를 작성하며, 인터페이스와 구현클래스를 구분하여 개발합니다.
ex) CategoryService /CategoryServiceImpl
서비스 어노테이션
클래스 레벨에 아래의 어노테이션을 사용합니다.
어노테이션 | 설명 |
@Service | 서비스 클래스임을 표시하는 springframework bean 어노테이션. |
@Slf4j | 로그 작성을 위해서 사용하는 lombok annotation. |
@RequiredArgsConstructor | 생성자 주입 편의를 위해 lombok 어노테이션입니다. |
CategoryServiceImpl.java
CategoryService.java
메소드 구성
서비스 메소드는 리포지토리 메소드 호출, 타 서비스 api 호출 및 기타 업무로직 처리 등의 작업을 실행한 후 결과를 반납합니다. 핵심 업무로직이 구현되도록 구성합니다.
트랜잭션 처리
등록/수정/삭제 서비스 메소드에 자동으로 트랜잭션이 동작하도록 AOP가 설정됩니다.
서비스 클래스(service 패키지의 *ServiceImpl 명칭의 클래스)의 메소드 중 register/modify/delete/save 이름으로 시작되는 public 메소드는 자동으로 트랜잭션이 걸리게 됩니다.
트랜잭션이 정상 동작하려면 메소드 명칭에 오타가 없도록 유의해야 합니다.
Mapper 작성
Mapper interface 작성
DB 테이블별로 1개의 ***Mapper와 1개의 ***TrxMapper를 작성합니다.
***Mapper에는 select문을 ***TrxMapper에는 insert/update/delete문을 작성합니다.
***Mapper는 readonly 데이터 소스가 적용되고, ***TrxMapper는 read/write 데이터 소스가 적용됩니다.
readonly 맵퍼에 insert/update/delete문을 작성한 경우 오류가 발생하게 되므로 반드시 구분하여 작성해야합니다.
Mapper는 interface 자바파일과 sql mapper xml파일 두개로 작성합니다.
Interface 에는 호출될 메소드 시그니처를 기록하고 SQL은 mapper xml에 기술합니다.
CategoryMapper.java
Mapper xml 작성
Mapper 메소드 호출시 실행될 SQL을 작성합니다.
CategoryMapper.xml
DTO/Entity 작성
Request DTO 작성
Request DTO는 조회/검색 컨트롤러 메소드의 파라미터를 담는 Bean객체입니다.
조회/검색 조건 파라미터가 존재하는 메소드 작성 시 Request DTO를 작성하여 요청 파라미터 값을 받아야 합니다.
DTO 클래스는 BaseCommonEntity 클래스를 상속받아 생성합니다. 유사한 조회/검색 조건을 갖는 메소드들에 공통으로 사용할 수 있습니다.
BaseCommonEntity 클래스에는 생성자/수정자/생성일시/수정일시/페이징 관련 정보/엑셀다운로드 관련정보 등을 포함하고 있습니다.
DTO 클래스에는 @Alias 어노테이션을 사용하여 Mapper 의 타입 축약어로 사용할 수 있도록합니다.
컨트롤러 메소드에서 받은 정보를 별도의 수정없이 서비스/Mapper 메소드에서 사용할 수 있습니다.
serialVersionUID는 반드시 UUID를 생성하여 사용합니다.
eclipse의 code assist 기능을 사용하면 쉽게 생성하실 수 있습니다.
PrDispGrpBaseRequest.java
Response DTO 작성
Response DTO는 조회/검색 컨트롤러 메소드의 결과값을 담는 Bean객체입니다.
조회/검색 결과가 존재하는 메소드 작성 시 Response DTO를 작성하여 요청 결과값을 뷰 페이지에 전달해야 합니다.
DTO 클래스는 BaseCommonEntity 클래스를 상속하지 않습니다.
DTO 클래스에는 @Alias 어노테이션을 사용하여 Mapper 메소드의 리턴타입으로 사용할 수 있도록 합니다.
Mapper 메소드의 조회결과를 그래도 서비스/컨트롤러/뷰에서 사용할 수 있습니다.
serialVersionUID는 반드시 UUID를 생성하여 사용합니다.
eclipse의 code assist 기능을 사용하면 쉽게 생성하실 수 있습니다.
PrDispGrpBaseResponse.java
Entity 작성
Entity 클래스는 등록/수정/삭제 컨트롤러 메소드의 파라미터를 담는 Bean객체입니다.
등록/수정/삭제 메소드 작성 시 Entity 클래스를 통해서 요청 파라미터 값을 받습니다.
Entity 클래스는 필드와 DB테이블 필드와 동일하게 구성합니다.
Entity 클래스는 BaseCommonEntity 클래스를 상속받아 생성합니다.
BaseCommonEntity 클래스의 생성자/수정자/생성일시/수정일시 등의 필드가 주로 사용됩니다.
Entity 클래스에는 @Alias 어노테이션을 사용하여 Mapper xml에서 사용할 수 있도록 합니다.
serialVersionUID는 반드시 UUID를 생성하여 사용합니다.
eclipse의 code assist 기능을 사용하면 쉽게 생성하실 수 있습니다.
PrDispCtgBase.java
오류처리
오류 발생 시 ApiException() 을 throw합니다. 생성자 파라미터로 ApiError을 지정합니다.
ApiError는 enumeration으로 오류유형과 메세지key를 포함한 오류상수들로 구성되어있습니다.
예를 들어 다음과 같이 ApiException이 throw된다면,
SampleClass.java
현재 ApiError는 다음과 같이 정의 되어있습니다.
ApiError.java
메세지를 위한 common.properties는 다음과 같습니다.
common.error.emptyParameter = 파라미터 값이 없습니다: {0} common.error.invalidParameter = 파라미터가 올바르지 않습니다. common.error.dataNotFound = 데이터가 존재하지 않습니다. common.error.bindingError = 파라미터 바인딩 오류입니다. common.error.bindingErrorNotNull = 파라미터 바인딩 오류입니다: not null common.error.unknown = 알 수 없는 오류입니다. common.message.success = 성공 |
프로퍼티
application.yml: 스프링 설정값이나 base 클래스 설정값을 관리합니다.
공통개발자가 관리하는 프로퍼티 항목들을 기록합니다.
config/application-<profile명>.properties: 업무개발 시 필요한 프로퍼티 항목들을 관리합니다.
각각의 업무개발자가 필요 시 항목을 추가할 수 있습니다.
java에서 프로퍼티 사용
@Value 어노테이션 사용: 클래스 맴버필드에 @Value 어노테이션을 붙여서 프로퍼티 값을 가져올 수 있습니다. 추천하는 방식은 다음과 같습니다.
@Value("${app.apiUrl.system}") private String systemApiUrl; |
environment 빈을 통한 조회 String uploadDomain = environment.get("domain.baseUrl")
메시지 사용
메시지는 src/main/resources/message 폴더에 관리합니다. 업무 대분류별로 개별 폴더를 생성하고 컨트롤러별로 메시지 파일을 관리한다.
java에서 메시지 사용
MessageResolver 클래스를 사용하여 메세지를 조회합니다.
메세지 key를 통해서 조회할 수도 있고, AdminError enum을 사용할 수도 있습니다.
메세지는 다국어를 지원하며, 보통은 LocalContext에 저장된 locale에 따라 언어를 구별하지만, 메소드 호출 시 Locale을 직접 지정할 수도 있습니다.
MessageResolver 클래스의 메소드 형식은 다음과 같습니다.
MessageResolver.java
로그
로깅 라이브러리 - logback 사용
로깅 라이브러리로는 logback이 사용되며, 로그 설정은 logback-spring.xml 파일에 기록됩니다.
로깅 라이브러리 사용법은 slf4j 인터페이스를 따릅니다. 다음절의 @Slf4j 어노테이션 사용과 같이 진행합니다.
로그작성 - @Slf4j lombok 어노테이션
로그작성이 필요한 클래스에 @Slf4j 어노테이션을 클래스레벨에 붙여줍니다. 로그는 log 객체를 통해서 작성합니다.
log.debug(“로그테스트 합니다: {}”, obj1); log.info(“로그테스트 합니다: {}”, obj1); log.warn(“로그테스트 합니다: {}”, obj1); log.error(“로그테스트 합니다: {}”, obj1); |
로그 레벨 사용
error 레벨: 오류상황인 경우 error 레벨로 로그를 작성합니다. debug 레벨: 디버그 용으로 출력하는 경우 debug 레벨로 로그를 작성합니다.
로그 조회
로컬 개발 환경에서는 콘솔로 출력되는 로그를 조회하면 되며, 파일로도 조회가능합니다.
로그파일의 기본경로는 c:/X2BEE-DEV/log/x2bee-***.log 입니다.
개발 환경이나 운영 환경에서는 설정된 kibana를 통해서 로그 조회가 가능합니다.
kibana 접속정보는 별도로 확인해 주세요.
마스킹(Masking)
동작원리
Request DTO에 @Masking 필드 어노테이션 적용. 적용시 type을 지정. SQL 조회 시 해당 @Masking 적용된 DTO필드에 대해 지정된 type유형의 masking이 자동 적용됩니다.
MaskingCUD.java
마스킹 유형
마스킹유형 | 유형코드 | 최소요건 | 설명 |
이름 | NAME_KR | 이름 두번째 마스킹 | 홍*동/을*문덕 |
영문이름 | NAME_EN | 이름 중 첫번째와 마지막 알파벳 제외하고 마스킹 | John Smith → J**h Smith(두개 이하 알파벳인 경우 첫번재 알파벳 남김) |
전화번호 | MOBILE_NUM | 중간번호 전체 마스킹 | 010****2134 |
주소 |
| 동이하주소 전체 마스킹 길이하주소 전체 마스킹 | 서울 강남구 압구정동 **** 서울 강남구 압구정로 **** |
ADDRESS_DTL |
| 주소상세 전체 마스킹 | |
이메일 | 아이디 4번째부터 끝까지 마스킹 | abc***@********** | |
주민번호 | RRN | 주민번호 뒤 7자리 이상 마스킹 | 801212-******* |
생년월일 | BIRTH | 일 자리 마스킹 | 1980-12-** |
운전면허번호 | LICENSE | 5번째부터 6자리이상 마스킹 | 서울 95-******-61 또는 11-95-******-61 |
여권번호 | PASSPORT | 뒤 4자리이상 마스킹 | M9999**** |
현금영수증카드 | CARD | 9번째부터 4자리이상 마스킹 | 1544-2020-****-123456 |
신용카드(14자리) | CARD | 8번째부터 4자리이상 마스킹 | 9500-0012-****-0000 9500-001*-****-*000 9500-00**-****-0000 |
기타카드(11자리) | CARD | 7번째부터 4자리이상 마스킹 | 9500-00**-**0 |
기타카드(13~19자리) | CARD | 8번째부터 4자리이상 마스킹 | 9500-0012-****-0000 9500-001*-****-*000 9500-00**-****-0000 |
사업자등록번호 | BNO | 3번째부터 4자리 이상 마스킹 | 12*-**-*1234 |
계좌번호 | ACTN | 6번째부터 끝까지 마스킹 | 12345********** |
QR코드 | QRCODE | 5번째부터 4자리이상 마스킹 | 126-0-****-1234 |
IP | IP | 7번째부터 3자리 이상 마스킹 | 123.123.***.123 |
ID | ID | 4자리부터 끝까지 마스킹 | kim****** |
MaskingUtils 사용
개별 데이터 마스킹이 필요한 경우 MaskingUtils 사용할 수 있습니다.
메소드 | 설명 |
String masking(String src, int startIdx) | 문자의 startIdx에서 끝까지 마스킹 |
String masking(String src, int startIdx, int length) | 문자의 startIdx에서 length 길이만큼 마스킹 |
DB 데이터 필드 암복화
동작원리
Request DTO 및 Entity 의 암호화 대상 필드에 @Encrypt 필드 어노테이션 적용을 적용합니다.
Mapper에서 SQL insert/update 시 해당 @Encrypt 적용된 필드가 암호화되어 DB에 저장되고, Mapper SQL에서 select 시 적용될 필드가 복호화 되어 조회됩니다.
다음은 @Encrypt 적용 샘플 코드입니다.
적용알고리즘
암복화 개발에는 AES-256-GCM을 사용하였으며, 적용 알고리즘은 다음과 같습니다.
AES 알고리즘 세트 | ||||
암호화 알고리즘 | 데이터 암호화 키 길이(비트) | 키 추출 알고리즘
| 서명 알고리즘 | 기간 약정 |
AES-256-GCM | 256 | HKDF (SHA-384 포함) | P-384 및 SHA-384 이 포함된 ECDSA | HKDF (SHA-512 포함) |
암호문 데이터 길이
암호화된 데이터의 길이는 평문 보다 길어져 DB 필드 설계 시 해당사항을 고려해 설계해야 합니다. 암호문의 데이터 길이 계산식은 다음과 같습니다.
암호화필드길이 = 880 + (원래필드길이 * 4/3) |
EncryptUtils 사용
개별 암호화/복호화가 필요한 경우 EncryptUtils static 메소드를 사용할 수 있습니다.
암호화 key 환경변수
암호화/복호화 시 사용될 key는 application.yml에 관리 됩니다.
암호화 키의 경우 32자리를 입력하면 됩니다.
메소드 권한 및 로그인 사용자 정보
getSampleUser.java
컨트롤러 메소드에 @Secured("ROLE_MEMBER") 메소드 어노테이션을 사용하면 회원 로그인된 경우만 진입되도록 처리됩니다.
로그인 안된 상태에서 해당 메소드 호출 시 HTTP_STATUS 403 FORBIDDEN 오류가 발생합니다.
컨트롤러 메소드에서 로그인 사용자 정보를 얻기위해서 @AuthenticationPrincipal 파리미터 어노테이션을 사용하면 됩니다.
userDetail.getMbrNo()를 통해서 회원번호 조회가 가능합니다.
Swagger3 @Schema
어노테이션 | 속성 | 설명 |
@Schema | description | 한글명 |
defaultValue | 기본값 | |
allowableValues | 허용가능한 값(열거형으로 정의 가능할 경우 설정) | |
example | 예시값 |
Request, Response DTO 작성시 위와 같은 어노테이션과 속성을 설정하여 Swagger UI를 통해 확인 할 수 있습니다.