Skip to content

🌱 Spring boot framework customize project

Notifications You must be signed in to change notification settings

discphy/project-base

Β 
Β 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

51 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

project-base

λ°”λ₯Έμ•„μ΄μ˜€ 곡톡 ν”„λ ˆμž„μ›Œν¬

λͺ©μ°¨

버전 정보

[μžλ°”] java : 1.8
[μŠ€ν”„λ§ λΆ€νŠΈ] org.springframework.boot : 2.7.12
[μ „μžμ •λΆ€ ν”„λ ˆμž„μ›Œν¬] org.egovframework : 4.2 

μ˜μ‘΄μ„±

[build.gradle]

dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-web'
	implementation 'org.springframework.boot:spring-boot-starter-webflux'
	implementation 'org.springframework.boot:spring-boot-starter-security'

    implementation("org.egovframe.rte:org.egovframe.rte.bat.core:4.2.0") {
		exclude group: 'org.egovframe.rte', module: 'org.egovframe.rte.fdl.logging'
	}

	implementation 'org.apache.commons:commons-collections4:4.4'
	implementation 'org.apache.commons:commons-text:1.10.0'
	implementation 'org.apache.poi:poi-ooxml:5.2.4'

	compileOnly 'org.projectlombok:lombok'
	annotationProcessor 'org.projectlombok:lombok'
	testImplementation 'org.springframework.boot:spring-boot-starter-test'
	testImplementation 'org.springframework.security:spring-security-test'
	testImplementation 'io.projectreactor:reactor-test'
	testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
}

라이브러리 생성

Nexusλ₯Ό κ³ λ €ν•˜κ³  μžˆμ§€λ§Œ ν˜„μž¬λŠ” jar둜 제곡

[build.gradle]

plugins {
	id 'java'
	id 'org.springframework.boot' version '2.7.12'
	id 'io.spring.dependency-management' version '1.0.15.RELEASE'
	id 'maven-publish'
	id 'com.github.johnrengelman.shadow' version '7.1.2'
}

...(μ€‘λž΅)

shadowJar {
    archiveFileName.set('project-base-0.0.1-all.jar')
}

publishing {
	publications {
		mavenJava(MavenPublication) {
			artifact shadowJar
		}
	}
}

com.github.johnrengelman.shadow : jar 파일 생성 μ‹œ 전체 μ˜μ‘΄μ„±(dependencies) 포함

[jar 파일 생성 방법]

./gradlew shadowJar 

μœ„μ˜ λͺ…λ Ήμ–΄ μ‹€ν–‰ μ‹œ, build/libs κ²½λ‘œμ— project-base-0.0.1-all.jar μƒμ„±λœλ‹€.

ν”„λ ˆμž„μ›Œν¬ λͺ¨λ“ˆ

μžμ„Έν•œ 예제 μ½”λ“œλŠ” project-sample μ—μ„œ μ„€λͺ…ν•œλ‹€.

API

io.bareun.base.api

μ™ΈλΆ€ APIλ₯Ό ν˜ΈμΆœν•˜κΈ° μœ„ν•œ λͺ¨λ“ˆ (동기, 비동기 지원)

/**
 * {@link WebClient}λ₯Ό μ‚¬μš©ν•˜μ—¬ μ›Ή API ν˜ΈμΆœμ„ μˆ˜ν–‰ν•˜λŠ” ν΄λΌμ΄μ–ΈνŠΈμž…λ‹ˆλ‹€.
 */
@Slf4j
@Component
public class WebApiClient {

    private final WebClient webClient;
    
    ...(μ€‘λž΅)

    /**
     * 주어진 API μš”μ²­μ„ λ™κΈ°μ μœΌλ‘œ ν˜ΈμΆœν•˜κ³  응닡을 λ°˜ν™˜ν•©λ‹ˆλ‹€.
     *
     * @param <T> 응닡 본문의 νƒ€μž…
     * @param request ν˜ΈμΆœν•  API μš”μ²­
     * @return API μš”μ²­μ˜ 응닡 λ³Έλ¬Έ
     */
    public <T> T callReturn(ApiRequest<T> request) {
        return retrieve(request).block();
    }

    /**
     * 주어진 API μš”μ²­μ„ λΉ„λ™κΈ°μ μœΌλ‘œ ν˜ΈμΆœν•©λ‹ˆλ‹€.
     *
     * @param <T> 응닡 본문의 νƒ€μž…
     * @param request ν˜ΈμΆœν•  API μš”μ²­
     */
    public <T> void call(ApiRequest<T> request) {
        retrieve(request).subscribe(request::subscribe, request::error);
    }
    
    ...(μ€‘λž΅)
}

WebClientλ₯Ό μ‚¬μš©ν•˜μ—¬ APIλ₯Ό ν˜ΈμΆœν•˜λ©° callReturn(ApiRequest<T> request)κ³Ό call(ApiRequest<T> request) λŠ” 각각 동기, 비동기λ₯Ό ν˜ΈμΆœν•˜λŠ” λ©”μ„œλ“œμ΄λ‹€.

/**
 * API μš”μ²­μ„ ν‘œν˜„ν•˜λŠ” μΈν„°νŽ˜μ΄μŠ€μž…λ‹ˆλ‹€.
 *
 * @param <T> 응닡 νƒ€μž…
 */
public interface ApiRequest<T> {

    /**
     * HTTP λ©”μ„œλ“œλ₯Ό λ°˜ν™˜ν•©λ‹ˆλ‹€.
     *
     * @return μš”μ²­μ— μ‚¬μš©λ  HTTP λ©”μ„œλ“œ
     */
    HttpMethod getMethod();

    /**
     * μš”μ²­ν•  URL을 λ°˜ν™˜ν•©λ‹ˆλ‹€.
     *
     * @return μš”μ²­ν•  URL
     */
    String getUrl();

    /**
     * μš”μ²­ 본문을 λ°˜ν™˜ν•©λ‹ˆλ‹€.
     *
     * @return μš”μ²­ λ³Έλ¬Έ
     */
    Object getBody();

    /**
     * 응닡 νƒ€μž…μ„ λ°˜ν™˜ν•©λ‹ˆλ‹€.
     *
     * @return 응닡 νƒ€μž… 클래슀
     */
    Class<T> getResponseType();
    
    ...(μ€‘λž΅)
}

API μš”μ²­ μΈν„°νŽ˜μ΄μŠ€, μΈν„°νŽ˜μ΄μŠ€ κ΅¬ν˜„ν•œ 클래슀둜 WebApiClient의 λ©”μ†Œλ“œλ₯Ό ν˜ΈμΆœν•œλ‹€.

μΆ”κ°€λ‘œ, Content-Type: application/jsonλ₯Ό μ§€μ›ν•˜λŠ” JsonApiRequest μΈν„°νŽ˜μ΄μŠ€μ™€ νŽΈλ¦¬ν•˜κ²Œ μΈμŠ€ν„΄μŠ€λ₯Ό μƒμ„±ν•˜λŠ” λΉŒλ” 클래슀 ApiRequestBuilder도 μ œκ³΅ν•œλ‹€.

곡톡

io.bareun.base.common

κ³΅ν†΅μœΌλ‘œ μ‚¬μš©ν•˜λŠ” Map ν΄λž˜μŠ€μ™€ 곡톡 Controller 응닡 클래슀, 자주 μ‚¬μš©ν•˜λŠ” μœ ν‹Έ 클래슀

/**
 * ν‚€-κ°’ μŒμ„ λ³΄κ΄€ν•˜κ³  μ‘°μž‘ν•˜λŠ” 데 μ‚¬μš©λ˜λŠ” DTO 맡 ν΄λž˜μŠ€μž…λ‹ˆλ‹€.
 * 기본적으둜 ν‚€λ₯Ό camelCase둜 λ³€ν™˜ν•©λ‹ˆλ‹€.
 */
public class BaseMap extends HashMap<String, Object> {
    ...(μ€‘λž΅)
}

κ³΅ν†΅μœΌλ‘œ μ‚¬μš©ν•˜λŠ” Map 클래슀둜, ν‚€λŠ” camelCase둜 λ³€ν™˜μ„ μ‹œν‚΅λ‹ˆλ‹€. νŽΈν•˜κ²Œ μ‚¬μš© ν•œ org.apache.commons.collections4.MapUtils에 μ˜κ±°ν•œ λ©”μ†Œλ“œλ„ μΆ”κ°€ν•˜μ˜€μŠ΅λ‹ˆλ‹€.

/**
 * BaseMap ν΄λž˜μŠ€μ— λŒ€ν•œ ν…ŒμŠ€νŠΈ ν΄λž˜μŠ€μž…λ‹ˆλ‹€.
 */
class BaseMapTest {

    /**
     * JSON λ¬Έμžμ—΄ νŒŒμ‹±μ„ ν…ŒμŠ€νŠΈν•˜λŠ” λ©”μ„œλ“œμž…λ‹ˆλ‹€.
     * JSON λ¬Έμžμ—΄μ„ BaseMap 객체둜 λ³€ν™˜ν•˜κ³ ,
     * λ³€ν™˜λœ BaseMap 객체의 값을 ν™•μΈν•©λ‹ˆλ‹€.
     */
    @Test
    void jsonParsing() {
        String json = "{\n" +
                "  \"search_map\": {\n" +
                "    \"string_value\": \"formal_contents\",\n" +
                "    \"long_value\": 1203405603,\n" +
                "    \"double_value\": 1.2,\n" +
                "    \"integer_value\": 10,\n" +
                "    \"map_value\" : {\n" +
                "      \"hello_world\" : \"I'm project base\"\n" +
                "    },\n" +
                "    \"list_string_value\": [\n" +
                "      \"one\",\n" +
                "      \"two\"\n" +
                "    ],\n" +
                "    \"list_map_value\": [\n" +
                "      {\n" +
                "        \"key\": \"value\"\n" +
                "      }\n" +
                "    ]\n" +
                "  }\n" +
                "}";

        BaseMap baseMap = BaseMap.of(json);

        System.out.println("baseMap = " + baseMap);

        assertThat(baseMap).isNotNull();
        assertThat(baseMap.getMap("searchMap")).isNotNull();
        assertThat(baseMap.getMap("searchMap").getString("stringValue")).isEqualTo("formal_contents");
        assertThat(baseMap.getMap("searchMap").getLong("longValue")).isEqualTo(1203405603);
        assertThat(baseMap.getMap("searchMap").getDouble("doubleValue")).isEqualTo(1.2);
        assertThat(baseMap.getMap("searchMap").getInteger("integerValue")).isEqualTo(10);
        assertThat(baseMap.getMapByKeys("searchMap", "mapValue").getString("helloWorld")).isEqualTo("I'm project base");
        assertThat(baseMap.getMap("searchMap").getList("listStringValue").get(0)).isEqualTo("one");

        List<BaseMap> list = baseMap.getMap("searchMap").getMapList("listMapValue");
        assertThat(list.size()).isEqualTo(1);

        BaseMap item = list.get(0);
        assertThat(item.getString("key")).isEqualTo("value");
    }
}

json λ¬Έμžμ—΄μ„ BaseMap으둜 λ³€ν™˜ν•˜κ³  Key값을 μ΄μš©ν•˜μ—¬ Value값을 μ°ΎλŠ” ν…ŒμŠ€νŠΈ μ½”λ“œ μž…λ‹ˆλ‹€. μ‚¬μš© 예제둜 μ°Έκ³ 

/**
 * API 응닡을 μœ„ν•œ ν΄λž˜μŠ€μž…λ‹ˆλ‹€.
 * <p>
 * 이 ν΄λž˜μŠ€λŠ” API μš”μ²­μ— λŒ€ν•΄ λ°˜ν™˜ν•  데이터 κ·œκ²©μ„ μ •μ˜ν•©λ‹ˆλ‹€.
 */
@Data
@Builder
@JsonInclude(NON_NULL)
public class ApiResponse<T> {

    /**
     * 응닡 μ½”λ“œ
     */
    private final int code;

    /**
     * 응닡 λ©”μ‹œμ§€
     */
    private final String message;

    /**
     * API μ‘λ‹΅μ˜ κ²°κ³Ό 데이터λ₯Ό 포함
     */
    private final T result;

    /**
     * μ‹€νŒ¨ 응닡을 μƒμ„±ν•˜λŠ” λ©”μ†Œλ“œ.
     *
     * @param code    응닡 μ½”λ“œ
     * @param message 응닡 λ©”μ‹œμ§€
     */
    public static ApiResponse<?> fail(int code, String message) {
        return ApiResponse.builder()
                .code(code)
                .message(message)
                .build();
    }

    /**
     * 성곡 응닡을 μƒμ„±ν•˜λŠ” λ©”μ†Œλ“œ.
     *
     * @param result κ²°κ³Ό 데이터
     */
    public static <T> ApiResponse<T> success(T result) {
        return ApiResponse.<T>builder()
                .code(HttpStatus.OK.value())
                .message(HttpStatus.OK.getReasonPhrase())
                .result(result)
                .build();
    }

    /**
     * κ²°κ³Ό 데이터 μ—†λŠ” 성곡 응닡을 μƒμ„±ν•˜λŠ” λ©”μ†Œλ“œ.
     */
    public static <T> ApiResponse<T> success() {
        return ApiResponse.<T>builder()
                .code(HttpStatus.OK.value())
                .message(HttpStatus.OK.getReasonPhrase())
                .build();
    }
}

λ‹€μŒκ³Ό 같은 json κ²°κ³Ό 값을 곡톡 처리 ν•œλ‹€. 정상 μ‘λ‹΅μ‹œ success λ©”μ†Œλ“œλ₯Ό μ΄μš©ν•˜μ—¬ λ¦¬ν„΄ν•˜λ©΄ λœλ‹€.

{
  "code" : (int),
  "message" : (String),
  "result" : (T)
}

μœ ν‹Έ 클래슀

  • ObjectMapperUtils : JSON 객체λ₯Ό λ³€ν™˜ν•˜λŠ” μœ ν‹Έ 클래슀 (String-T / Object-T)
  • RequestUtils : HttpServletRequest 및 HttpSession μ²˜λ¦¬ν•˜λŠ” μœ ν‹Έ 클래슀
  • ResponseUtils : HttpServletResponse μœ ν‹Έ 클래슀
  • SecurityUtils : Spring Security μœ ν‹Έ 클래슀

μ˜ˆμ™Έ 처리

io.bareun.base.exception

기본적으둜 μ˜ˆμ™Έ μ²˜λ¦¬μ— μ‚¬μš©ν•˜λŠ” μ—λŸ¬ μ½”λ“œ 및 Exceptionμ •μ˜, μ˜ˆμ™Έ ν•Έλ“€λŸ¬ ν΄λž˜μŠ€κ°€ 제곡

public interface ErrorCode {

    int getCode();

    String getMessage();
}

κΈ°λ³Έ μ—λŸ¬ μ½”λ“œ μΈν„°νŽ˜μ΄μŠ€μ΄λ‹€. 각 ν”„λ‘œμ νŠΈμ—μ„œ ErrorCodeλ₯Ό κ΅¬ν˜„ν•΄μ„œ μ‚¬μš©ν•˜λ©΄ λœλ‹€. 기본으둜 ErrorCodeλ₯Ό κ΅¬ν˜„ν•œ μ—λŸ¬μ½”λ“œλŠ” λ‹€μŒκ³Ό κ°™λ‹€.

/**
 * ErrorCodeλŠ” μ˜ˆμ™Έ μ²˜λ¦¬μ— μ‚¬μš©λ˜λŠ” 였λ₯˜ μ½”λ“œλ₯Ό μ •μ˜ν•˜λŠ” μ—΄κ±°ν˜• ν΄λž˜μŠ€μž…λ‹ˆλ‹€.
 * 각 μ—΄κ±° μƒμˆ˜λŠ” 였λ₯˜ μ½”λ“œμ™€ λ©”μ‹œμ§€λ₯Ό 가지고 μžˆμŠ΅λ‹ˆλ‹€.
 */
@Getter
@RequiredArgsConstructor
public enum BaseErrorCode implements ErrorCode {

    /**
     * HTTP 40x μ½”λ“œ 기반 5자리
     */
    BAD_REQUEST(40000, "잘λͺ»λœ μš”μ²­ κ°’ μž…λ‹ˆλ‹€."),
    REQUIRED(40001, "%s 값은 ν•„μˆ˜μž…λ‹ˆλ‹€."),
    VALIDATE(40002, "%s 값이 μ˜¬λ°”λ₯΄μ§€ μ•ŠμŠ΅λ‹ˆλ‹€."),

    /**
     * HTTP 50x μ½”λ“œ 기반 5자리
     */
    UNKNOWN(50000, "μ•Œ 수 μ—†λŠ” μ—λŸ¬μž…λ‹ˆλ‹€."),
    ;

    private final int code;
    private final String message;
}

μ•žμ˜ 3μžλ¦¬λŠ” HTTP Status Code μ½”λ“œλ‘œ κ΅¬μ„±ν•˜λ©°, λ’€μ˜ 2μžλ¦¬λŠ” μ»€μŠ€ν…€ μ½”λ“œμ΄λ‹€.

/**
 * BusinessException은 λΉ„μ¦ˆλ‹ˆμŠ€ λ‘œμ§μ—μ„œ λ°œμƒν•  수 μžˆλŠ” μ˜ˆμ™Έλ₯Ό λ‚˜νƒ€λ‚΄λŠ” ν΄λž˜μŠ€μž…λ‹ˆλ‹€.
 * RuntimeException을 상속받아 unchecked μ˜ˆμ™Έλ‘œ μ •μ˜λ˜μ–΄ μžˆμŠ΅λ‹ˆλ‹€.
 */
@Getter
public class BusinessException extends RuntimeException {

    private final ErrorCode errorCode;

    /**
     * 주어진 λ©”μ‹œμ§€λ₯Ό 가지고 기본적인 UNKNOWN μ—λŸ¬ μ½”λ“œλ‘œ BusinessException을 μƒμ„±ν•©λ‹ˆλ‹€.
     *
     * @param message μ˜ˆμ™Έ λ©”μ‹œμ§€
     */
    public BusinessException(String message) {
        super(message);
        this.errorCode = BaseErrorCode.UNKNOWN;
    }

    /**
     * 주어진 μ—λŸ¬ μ½”λ“œλ‘œ BusinessException을 μƒμ„±ν•©λ‹ˆλ‹€.
     *
     * @param errorCode μ—λŸ¬ μ½”λ“œ
     */
    public BusinessException(ErrorCode errorCode) {
        super(errorCode.getMessage());
        this.errorCode = errorCode;
    }

    /**
     * 주어진 μ—λŸ¬ μ½”λ“œμ™€ 좔가적인 인자λ₯Ό μ‚¬μš©ν•˜μ—¬ BusinessException을 μƒμ„±ν•©λ‹ˆλ‹€.
     * μ—λŸ¬ λ©”μ‹œμ§€λŠ” ν¬λ§·νŒ…λœ ν˜•νƒœλ‘œ μ œκ³΅λ©λ‹ˆλ‹€.
     *
     * @param errorCode μ—λŸ¬ μ½”λ“œ
     * @param args      ν¬λ§·νŒ…μ— μ‚¬μš©λ  μΈμžλ“€
     */
    public BusinessException(ErrorCode errorCode, Object... args) {
        super(String.format(errorCode.getMessage(), args));
        this.errorCode = errorCode;
    }
}

μœ„μ˜ ErrorCodeλ₯Ό 기반으둜 ν•˜λŠ” μ˜ˆμ™Έ ν΄λž˜μŠ€μ΄λ‹€. ν•΄λ‹Ή μ˜ˆμ™Έ ν΄λž˜μŠ€λŠ” μ—…λ¬΄κ΄€λ ¨λœ μ‚¬μš©μž μ—λŸ¬λ©”μ„Έμ§€λ₯Ό μ²˜λ¦¬ν•œλ‹€.

/**
 * ApiExceptionHandlerλŠ” Spring Web MVCμ—μ„œ λ°œμƒν•˜λŠ” μ˜ˆμ™Έλ₯Ό μ²˜λ¦¬ν•˜λŠ” ν΄λž˜μŠ€μž…λ‹ˆλ‹€.
 * RestControllerAdvice μ• λ…Έν…Œμ΄μ…˜μ„ μ‚¬μš©ν•˜μ—¬ λͺ¨λ“  @RestControllerμ—μ„œ λ°œμƒν•˜λŠ” μ˜ˆμ™Έλ₯Ό μ²˜λ¦¬ν•©λ‹ˆλ‹€.
 * 각 μ˜ˆμ™Έμ— 따라 μ μ ˆν•œ HTTP μƒνƒœ μ½”λ“œμ™€ λ©”μ‹œμ§€λ₯Ό λ°˜ν™˜ν•©λ‹ˆλ‹€.
 */
@Slf4j
@RestControllerAdvice(annotations = RestController.class)
public class ApiExceptionHandler {

    /**
     * BusinessException을 μ²˜λ¦¬ν•˜λŠ” λ©”μ„œλ“œμž…λ‹ˆλ‹€.
     *
     * @param e λ°œμƒν•œ BusinessException 객체
     * @return ApiResponse 객체 (μ‹€νŒ¨ 응닡)
     */
    @ExceptionHandler(BusinessException.class)
    public ApiResponse<?> buisnessException(BusinessException e) {
        log.error("ApiException buisnessException", e);
        return ApiResponse.fail(e.getErrorCode().getCode(), e.getMessage());
    }
    
    ...(μ€‘λž΅)
}

μœ„μ˜ μ˜ˆμ™Έ 클래슀λ₯Ό ν•Έλ“€λŸ¬ν•˜λŠ” ν΄λž˜μŠ€μ΄λ‹€. ApiResponse의 fail() λ©”μ†Œλ“œλ₯Ό λ°˜ν™˜ν•œλ‹€.

파일 처리

io.bareun.base.file

첨뢀 파일 및 μ—‘μ…€ 파일 μ—…λ‘œλ“œ, λ‹€μš΄λ‘œλ“œ μ§€μ›ν•˜λŠ” λͺ¨λ“ˆ

/**
 * FileManager μΈν„°νŽ˜μ΄μŠ€λŠ” 파일 관리 κΈ°λŠ₯을 μ •μ˜ν•˜λŠ” μΈν„°νŽ˜μ΄μŠ€μž…λ‹ˆλ‹€.
 * κ΅¬ν˜„ ν΄λž˜μŠ€μ—μ„œ 파일 μ—…λ‘œλ“œ, λ‹€μš΄λ‘œλ“œ, 파일λͺ… 생성 λ“±μ˜ κΈ°λŠ₯을 μ œκ³΅ν•©λ‹ˆλ‹€.
 */
public interface FileManager {

    /**
     * 파일이 μ €μž₯될 디렉토리 경둜λ₯Ό λ°˜ν™˜ν•©λ‹ˆλ‹€.
     *
     * @return 파일이 μ €μž₯될 디렉토리 경둜
     */
    String getDirectory();

    ...(μ€‘λž΅)

    /**
     * 주어진 MultipartFile을 μ—…λ‘œλ“œν•˜κ³  AttachUploadFile 객체둜 λ°˜ν™˜ν•©λ‹ˆλ‹€.
     *
     * @param file μ—…λ‘œλ“œν•  MultipartFile 객체
     * @return AttachUploadFile 객체
     */
    default AttachUploadFile upload(MultipartFile file) {
        validate(file);

        String originalFileName = file.getOriginalFilename();
        String storedFileName = createStoredFileName(originalFileName);

        FileUtils.upload(file, getFullPath(storedFileName));

        return AttachUploadFile.of(originalFileName, storedFileName);
    }

    /**
     * DownloadFile 객체λ₯Ό μ‚¬μš©ν•˜μ—¬ νŒŒμΌμ„ λ‹€μš΄λ‘œλ“œν•©λ‹ˆλ‹€.
     *
     * @param downloadFile λ‹€μš΄λ‘œλ“œν•  파일 정보λ₯Ό ν¬ν•¨ν•œ DownloadFile 객체
     * @param <T>          응닡 λ°”λ”” νƒ€μž…
     * @return ResponseEntity 객체둜 감싼 λ‹€μš΄λ‘œλ“œ κ²°κ³Ό
     */
    default <T> ResponseEntity<T> download(DownloadFile<T> downloadFile) {
        return ResponseEntity.ok().headers(downloadFile.getHeaders()).body(downloadFile.getBody());
    }
}

νŒŒμΌμ„ κ΄€λ¦¬ν•˜λŠ” μΈν„°νŽ˜μ΄μŠ€λ‘œ, μ €μž₯ 디렉토리λ₯Ό κ°€μ Έμ˜€λŠ” getDirectory()λ₯Ό ν•„μˆ˜λ‘œ κ΅¬ν˜„ν•΄μ•Όν•œλ‹€. μ—…λ‘œλ“œμ™€ λ‹€μš΄λ‘œλ“œ κΈ°λŠ₯을 κΈ°λ³Έ μ œκ³΅ν•œλ‹€.

/**
 * UploadFile μΈν„°νŽ˜μ΄μŠ€λŠ” μ—…λ‘œλ“œλœ 파일의 원본 파일λͺ…κ³Ό μ €μž₯된 파일λͺ…을 μ œκ³΅ν•˜λŠ” λ©”μ„œλ“œλ₯Ό μ •μ˜ν•©λ‹ˆλ‹€.
 */
public interface UploadFile {

    /**
     * μ—…λ‘œλ“œλœ 파일의 원본 파일λͺ…을 λ°˜ν™˜ν•©λ‹ˆλ‹€.
     *
     * @return 원본 파일λͺ…
     */
    String getOriginalFileName();

    /**
     * μ—…λ‘œλ“œλœ 파일의 μ €μž₯된 파일λͺ…을 λ°˜ν™˜ν•©λ‹ˆλ‹€.
     *
     * @return μ €μž₯된 파일λͺ…
     */
    String getStoredFileName();
}

μ—…λ‘œλ“œ 파일 μΈν„°νŽ˜μ΄μŠ€λ‘œ, μΈν„°νŽ˜μ΄μŠ€ κ΅¬ν˜„μ²΄λ‘œ AttachUploadFile와 ExcelUploadFile<T>λŠ” 각각 μ²¨λΆ€νŒŒμΌ, μ—‘μ…€νŒŒμΌ μ—…λ‘œλ“œ ν΄λž˜μŠ€μ΄λ‹€. μ—…λ‘œλ“œν•œ μ •λ³΄λ‘œ DBμ—μ„œ 관리될 수 μžˆλ‹€.

/**
 * DownloadFile μΈν„°νŽ˜μ΄μŠ€λŠ” 파일 λ‹€μš΄λ‘œλ“œ 정보λ₯Ό μ •μ˜ν•˜λŠ” μΈν„°νŽ˜μ΄μŠ€μž…λ‹ˆλ‹€.
 * κ΅¬ν˜„ ν΄λž˜μŠ€μ—μ„œλŠ” λ‹€μš΄λ‘œλ“œν•  파일λͺ…, HTTP 헀더, 응닡 λ°”λ””λ₯Ό μ œκ³΅ν•΄μ•Ό ν•©λ‹ˆλ‹€.
 *
 * @param <T> λ‹€μš΄λ‘œλ“œν•  λ°μ΄ν„°μ˜ νƒ€μž…
 */
public interface DownloadFile<T> {

    /**
     * λ‹€μš΄λ‘œλ“œν•  파일λͺ…을 λ°˜ν™˜ν•©λ‹ˆλ‹€.
     *
     * @return λ‹€μš΄λ‘œλ“œν•  파일λͺ…
     */
    String getDownloadFileName();

    /**
     * λ‹€μš΄λ‘œλ“œν•  νŒŒμΌμ— λŒ€ν•œ HTTP 헀더λ₯Ό λ°˜ν™˜ν•©λ‹ˆλ‹€.
     *
     * @return HTTP 헀더 객체
     */
    HttpHeaders getHeaders();

    /**
     * λ‹€μš΄λ‘œλ“œν•  λ°μ΄ν„°μ˜ 본문을 λ°˜ν™˜ν•©λ‹ˆλ‹€.
     *
     * @return λ‹€μš΄λ‘œλ“œν•  λ°μ΄ν„°μ˜ λ³Έλ¬Έ
     */
    T getBody();
}

λ‹€μš΄λ‘œλ“œ 파일 μΈν„°νŽ˜μ΄μŠ€λ‘œ, μΈν„°νŽ˜μ΄μŠ€ κ΅¬ν˜„μ²΄λ‘œ AttachDownloadFile와 ExcelDownloadFileλŠ” 각각 μ²¨λΆ€νŒŒμΌ, μ—‘μ…€νŒŒμΌ λ‹€μš΄λ‘œλ“œ ν΄λž˜μŠ€μ΄λ‹€.

κ΅¬ν˜„ 클래슀λ₯Ό μΈμŠ€ν„΄μŠ€ν•˜μ—¬ FileManager의 download λ©”μ†Œλ“œλ₯Ό ν˜ΈμΆœν•˜λ©΄ 응닡 κ°’μœΌλ‘œ 파일 λ‹€μš΄λ‘œλ“œκ°€ μ‹€ν–‰λœλ‹€.

/**
 * ExcelWriter μΈν„°νŽ˜μ΄μŠ€λŠ” Excel 파일 μž‘μ„±μ„ μœ„ν•œ κΈ°λŠ₯을 μ •μ˜ν•©λ‹ˆλ‹€.
 * <p>
 * κ΅¬ν˜„μ²΄λŠ” Excel 파일의 헀더 μŠ€νƒ€μΌ, 헀더 이름 및 값을 μž‘μ„±ν•˜κΈ° μœ„ν•œ λ©”μ„œλ“œλ₯Ό ν¬ν•¨ν•©λ‹ˆλ‹€.
 * </p>
 *
 * @param <T> Excel νŒŒμΌμ— μž‘μ„±ν•  객체의 νƒ€μž…
 */
public interface ExcelWriter<T> {

    /**
     * Excel νŒŒμΌμ— μž‘μ„±ν•  데이터 리슀트λ₯Ό λ°˜ν™˜ν•©λ‹ˆλ‹€.
     *
     * @return Excel νŒŒμΌμ— μž‘μ„±ν•  데이터 리슀트
     */
    List<?> getList();

    /**
     * Excel νŒŒμΌμ— μž‘μ„±ν•  객체의 클래슀 νƒ€μž…μ„ λ°˜ν™˜ν•©λ‹ˆλ‹€.
     *
     * @return Excel νŒŒμΌμ— μž‘μ„±ν•  객체의 클래슀 νƒ€μž…
     */
    Class<T> getType();
    
    ...(μ€‘λž΅)
}

μ—‘μ…€ λ‹€μš΄λ‘œλ“œ μ‹œ, List<?>의 데이터λ₯Ό μ—‘μ…€λ‘œ μ“°λŠ” μΈν„°νŽ˜μ΄μŠ€μ΄λ‹€. κΈ°λ³Έ κ΅¬ν˜„ 클래슀둜 DefaultExcelWriter제곡

파일 μœ ν‹Έ 클래슀

  • FileUtils : κΈ°λ³Έ 첨뢀 파일 μœ ν‹Έ 클래슀
  • ExcelFileUtils : μ—‘μ…€ 파일 κ΄€λ ¨ μœ ν‹Έ 클레슀

λ‘œκΉ…

io.bareun.base.log

@RestController에 λŒ€ν•œ HTTP λ‘œκΉ…μ„ κ³΅ν†΅μœΌλ‘œ μ œκ³΅ν•œλ‹€.

/**
 * ApiLoggingAspectλŠ” REST 컨트둀러의 HTTP μš”μ²­μ„ λ‘œκΉ…ν•˜κΈ° μœ„ν•œ Aspectμž…λ‹ˆλ‹€.
 * <p>
 * 이 ν΄λž˜μŠ€λŠ” @RestController μ–΄λ…Έν…Œμ΄μ…˜μ΄ 뢙은 클래슀 λ‚΄μ—μ„œ λ©”μ„œλ“œ 호좜 전에 HTTP μš”μ²­μ„ λ‘œκΉ…ν•©λ‹ˆλ‹€.
 * λ‘œκΉ…ν•  μ •λ³΄λ‘œλŠ” IP μ£Όμ†Œ, HTTP λ©”μ„œλ“œ, μš”μ²­ URL, 쿼리 슀트링, μš”μ²­ λ°”λ””κ°€ ν¬ν•¨λ©λ‹ˆλ‹€.
 */
@Slf4j
@Aspect
@Component
public class ApiLoggingAspect {

    /**
     * {@link org.springframework.web.bind.annotation.RestController} μ–΄λ…Έν…Œμ΄μ…˜μ΄ 뢙은 클래슀
     * λ‚΄μ˜ λͺ¨λ“  λ©”μ„œλ“œλ₯Ό 포인트컷으둜 μ„€μ •ν•©λ‹ˆλ‹€.
     */
    @Pointcut("within(@org.springframework.web.bind.annotation.RestController *)")
    public void restController() {
    }

    /**
     * ν¬μΈνŠΈμ»·μ—μ„œ μ§€μ •ν•œ λ©”μ„œλ“œ 호좜 전에 HTTP μš”μ²­μ„ λ‘œκΉ…ν•˜λŠ” λ©”μ„œλ“œμž…λ‹ˆλ‹€.
     *
     * @param joinPoint 쑰인 포인트 객체둜, 호좜된 λ©”μ„œλ“œμ™€ κ·Έ νŒŒλΌλ―Έν„° 등을 μΆ”μΆœν•˜λŠ” 데 μ‚¬μš©λ©λ‹ˆλ‹€.
     */
    @Before("restController()")
    public void httpLogging(JoinPoint joinPoint) {
        String remoteAddr = getRemoteAddr();
        String method = getMethod();
        String requestURL = getRequestURL();
        String queryString = getQueryString();

        String requestBody = getRequestBody(joinPoint);

        log.info("HTTP Logging IP : {} | Method : {} | URL : {} | Query : {} | Body {}",
                remoteAddr, method, requestURL, queryString, requestBody);
    }
    
    ...(μ€‘λž΅)
}

AOPλ₯Ό μ΄μš©ν•˜μ—¬ HTTP의 정보λ₯Ό λ‘œκΉ…ν•œλ‹€.

[둜그 포맷]

HTTP Logging IP : {} | Method : {} | URL : {} | Query : {} | Body {}

μƒ˜ν”Œ ν”„λ‘œμ νŠΈ

https://github.com/bareunio/project-sample

이슈

이슈 및 κ°œμ„ μ‚¬ν•­μ€ μ•„λž˜μ˜ 링크λ₯Ό 톡해 등둝 ν•΄μ£Όμ„Έμš”

https://github.com/bareunio/project-base/issues

About

🌱 Spring boot framework customize project

Resources

Stars

Watchers

Forks

Releases

No releases published

Sponsor this project

Packages

No packages published

Languages

  • Java 100.0%