제가 공부한 내용을 정리하는 블로그입니다.
아직 많이 부족하고 배울게 너무나도 많습니다. 틀린내용이 있으면 언제나 가감없이 말씀해주시면 감사하겠습니다😁

스프링 정리를 위한 포스팅입니다.
해당 포스팅은 @Slf4j 정리입니다.

 

로그가 뭐야?

로그(Log)는 애플리케이션이 실행되면서 발생하는 이벤트, 오류, 상태 정보 등을 기록하는 중요한 데이터입니다.
프로그램의 실행 흐름을 추적하고, 문제 발생 시 원인을 분석하는 데 필수적인 요소입니다.

로그가 중요한 이유

  1. 디버깅 & 문제 해결
    • 애플리케이션에서 오류가 발생했을 때, 로그를 통해 원인을 분석하고 해결할 수 있습니다.
    • 예를 들어, 사용자가 API 요청을 보냈을 때 응답이 늦거나 실패하는 경우 로그를 확인하면 어느 단계에서 문제가 발생했는지 파악할 수 있습니다.
  2. 운영 모니터링 & 시스템 분석
    • 운영 중인 서비스의 성능을 추적하고, 장애 발생 가능성을 미리 감지할 수 있습니다.
    • 로그 데이터는 시스템의 상태를 분석하고 최적화하는 데 중요한 역할을 합니다.
  3. 보안 & 감사(Audit Logging)
    • 사용자 활동 기록(로그인, 결제, 데이터 변경 등)을 남겨 보안 감사(Audit) 및 문제 발생 시 추적할 수 있습니다.
    • 보안 로그는 악의적인 접근을 감지하고, 사이버 공격을 방어하는 데 사용됩니다.

로그를 남기는 방법

  • Java에서는 System.out.println()을 사용하여 로그를 출력할 수 있지만, 이는 성능 저하 및 유지보수 어려움 등의 단점이 있습니다.
  • 대신, Slf4j와 같은 로깅 프레임워크를 활용하면 효율적이고 확장성 있는 로깅 시스템을 구축할 수 있습니다.

Slf4j란?

Slf4j(Simple Logging Facade for Java)는 Java 애플리케이션에서 로깅을 위한 추상화 인터페이스입니다.
쉽게 말해, 특정 로깅 프레임워크(Logback, Log4j 등)에 종속되지 않고 Slf4j를 통해 원하는 로깅 시스템을 선택하여 사용할 수 있도록 도와주는 역할을 합니다.

💡 Slf4j는 로깅을 위한 "추상화 인터페이스"이며, 실제 로깅은 Logback, Log4j와 같은 구현체가 수행합니다.

Slf4j와 Logback 관계

Slf4j 로깅 추상화 인터페이스 (로깅 방식만 제공, 직접 로그 출력 X)
Logback Slf4j의 기본 로깅 구현체 (Spring Boot 기본 제공)
Log4j 별도의 로깅 프레임워크 (Slf4j와 함께 사용할 수 있음)

 


Slf4j를 사용하는 이유

  • 특정 로깅 프레임워크에 종속되지 않음
    1. Slf4j를 사용하면 코드 수정 없이 Logback, Log4j 등 원하는 로깅 프레임워크를 쉽게 교체할 수 있습니다
  • System.out.println()보다 성능 우수
    1. System.out.println()은 단순한 콘솔 출력이지만, Slf4j는 다양한 출력 대상(파일, DB, 원격 서버 등)에 기록할 수 있습니다
    2. System.out.println()은 실행 속도가 느리지만, Slf4j는 비동기 로깅레벨 제어가 가능하여 성능 최적화에 유리합니다.
  • 로그레벨 지원
    1. Slf4j는 로그의 중요도에 따라 TRACE, DEBUG, INFO, WARN, ERROR 등의 로그 레벨을 제공하여 필요한 정보만 기록할 수 있도록 합니다.

로그 레벨설명

로그 레벨 설명
TRACE 가장 상세한 디버깅 로그
DEBUG 개발 시 필요한 디버깅 정보
INFO 운영 시 중요한 정보 로그
WARN 경고 로그 (문제 가능성 존재)
ERROR 심각한 오류 발생 로그

Slf4j 사용법

Lombok에서는 @Slf4j를 지원하여 간편하게 사용할 수 있습니다.
@Slf4j는 AOP(Aspect-Oriented Programming) 기능을 활용하여 자동으로 Logger 객체를 생성해줍니다.

import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@Slf4j // Logger 객체 자동 생성
@RestController
public class LogController {

    @GetMapping("/log")
    public String logExample(@RequestParam String message) {
        log.info("INFO 로그: {}", message);
        log.warn("WARN 로그: {}", message);
        log.error("ERROR 로그: {}", message);
        return "로그 출력 완료!";
    }
}

 

결과

포스트맨으로 요청
인텔리제이에서의 로그 확인

 

Slf4j는 Spring Boot에서 로깅을 더욱 유연하고 효율적으로 관리할 수 있도록 도와주는 필수적인 라이브러리입니다.
로깅은 디버깅, 모니터링, 보안, 성능 분석 등 여러 측면에서 필수적인 요소이므로, 올바르게 활용하는 것이 중요합니다.

이제 프로젝트에서 Slf4j를 적극 활용하여 가독성 높은 로그 관리를 해보세요!

 

 

코드

제가 공부한 내용을 정리하는 블로그입니다.
아직 많이 부족하고 배울게 너무나도 많습니다. 틀린내용이 있으면 언제나 가감없이 말씀해주시면 감사하겠습니다😁

스프링 정리를 위한 포스팅입니다.
해당 포스팅은 직렬화, 역직렬화 어노테이션 정리입니다.

 

직렬화? 역직렬화?

직렬화(Serialization)

직렬화객체를 JSON, XML, 바이너리 등 데이터 형식으로 변환하는 과정을 의미합니다.

이 과정을 통해 데이터를 네트워크로 전송하거나 데이터 스트림으로 전송하는데에 사용되빈다.

import com.fasterxml.jackson.databind.ObjectMapper;

public class Main {
    public static void main(String[] args) throws Exception {
        User user = new User("testUser", "password123");
        ObjectMapper objectMapper = new ObjectMapper();

        // 직렬화 (객체 → JSON)
        String json = objectMapper.writeValueAsString(user);
        System.out.println(json);
    }
}

// 출력
{
    "username": "testUser",
    "password": "password123"
}

 

역직렬화(Deserialization)

역직렬화JSON, XML, 바이너리 데이터를 객체로 변환하는 과정을 의미합니다.

이를 통해 비즈니스 로직에서 객체를 활용하여 사용할 수 있습니다.

import com.fasterxml.jackson.databind.ObjectMapper;

public class Main {
    public static void main(String[] args) throws Exception {
        String json = "{\"username\":\"testUser\",\"password\":\"password123\"}";
        ObjectMapper objectMapper = new ObjectMapper();

        // 역직렬화 (JSON → 객체)
        User user = objectMapper.readValue(json, User.class);
        System.out.println(user.getUsername());
    }
}

// 출력
testUser

 

@JsonIgonre

@JsonIgonre은 JSON 변환 과정에서 특정 필드를 제외할 때 사용됩니다.

import com.fasterxml.jackson.annotation.JsonIgnore;

public class User {
    private String username;

    @JsonIgnore
    private String password; // JSON 변환 시 제외됨

    public User(String username, String password) {
        this.username = username;
        this.password = password;
    }
    
    public String getUsername() { return username; }
    public String getPassword() { return password; }
}

// 출력
{
    "username": "testUser"
}

password 필드가 포함되지 않는 이유가 @JsonIgnore로 때문입니다.

보안 뿐만 아니라 무한 루프 방지 등의 용도로도 사용할 수 있습니다.

 

@JsonProperty

@JsonProperty는 직렬화 또는 역직렬화할 때 필드의 JSON 키 이름을 변경하는데 사용할 수 있습니다.

특정 조건에서만 JSON 처리를 제어할 수 있기에 더욱 활용도가 높습니다.

import com.fasterxml.jackson.annotation.JsonProperty;

public class User {
    private String username;

    @JsonProperty(access = JsonProperty.Access.WRITE_ONLY) // 역직렬화만 허용
    private String password;
}

// 출력
{
    "username": "testUser"
}

password는 API 응답에서는 제외되지만, JSON 응답을 받을 때는 사용이 가능합니다.

이렇게 내부적으로 보안을 위한 경우에도 사용할 수 있습니다. 

 

@JsonInclude

@JsonInclude는 특정 조건을 만족하는 필드만 JSON에 포함 할 수 있습니다. 예를 들어 null을 가진 필드를 제외하려면

import com.fasterxml.jackson.annotation.JsonInclude;

@JsonInclude(JsonInclude.Include.NON_NULL)
public class User {
    private String username;
    private String email;
}

// 출력: email이 null일 때
{
    "username": "testUser"
}

와 같이 구현할 수 있습니다.

 

@JsonIgnoreType

@JsonIgnoreType는 특정 타입의 모든 필드를 JSON 변환에서 제외할 수 있습니다.

객체 내부에 사용되는 숨겨야하는 필드에 대해서 JSON 변환을 제외합니다.

import com.fasterxml.jackson.annotation.JsonIgnoreType;

@JsonIgnoreType
public class SecretData {
    public String key = "secret";
}

public class User {
    public String username = "testUser";
    public SecretData secretData = new SecretData();
}

// 출력
{
    "username": "testUser"
}

 

결과

password 필드 직렬화하지않음
@JsonProperty 설정으로 역직렬화만 허용

 

코드

제가 공부한 내용을 정리하는 블로그입니다.
아직 많이 부족하고 배울게 너무나도 많습니다. 틀린내용이 있으면 언제나 가감없이 말씀해주시면 감사하겠습니다😁

자바 정리를 위한 포스팅입니다.
해당 포스팅은 어노테이션 정리입니다.

 

등장 배경

과거에는 소스파일은 java파일 설정 값을 XML파일에 명시하여 관리했습니다. 변경될 수 있는 데이터들을 코드가 아닌 외부 설정 파일(XML)에 분리하기 때문에, 재컴파일 없이도 쉽게 변경사항을 저장할 수 있었습니다.

하지만, 프로그램 작성을 위해 매번 많은 설정을 해야하며, 수 많은 설정파일들을 관리해야 했습니다.

 

그에 따라서 설정파일을 업데이트를 하지 않아 코드의 실행동작과 같은 부분들이 달라지거나 개발에 대해서 불편함이 많았습니다.

source code + 설정 파일

이렇듯 하나의 파일 내에서 코드와 설정을 관리할 수 있는 어노테이션이 등장했는데

 

자바 어노테이션(Java Annotation)은 자바 소스 코드에 추가하여 사용할 수 있는 메타데이터의 일종으로. @기호를 앞에 붙여서 사용하고, JDK 1.5 버전 이상에서 사용 가능합니다. 

 

어노테이션의 역할로는 컴파일러에게 코드 작성 문법 에러 체크하도록 정보제공, 런타임에 특정 기능을 실행하도록 정보 제공 등 하고 있습니다.

 

어노테이션 종류

어노테이션의 종류로는 세가지가 있는데,

  1. 표준 어노테이션
  2. 메타 어노테이션
  3. 사용자 정의 어노테이션

이 존재합니다

표준 어노테이션

표준 어노테이션은 자바가 기본적으로 제공해주는 어노테이션을 의미하는 것으로,

@Override, @Deprecated, @SuppressWarning, @FunctionIInterface 네 가지 어노테이션이 가장 많이 사용됩니다.

/**
 * {@code @Override} 어노테이션은 자식 클래스에서 부모 클래스의 메서드를 오버라이드할 때 사용됩니다.
 * 부모 클래스의 메서드와 일치하지 않으면 컴파일 에러가 발생합니다.
 */
class Parent {
    void run() {}
}

class Child extends Parent {
    @Override // 컴파일 에러! 잘못된 오버라이드
    void wrongOverride() {}
}


/**
 * {@code @Deprecated} 어노테이션은 해당 메서드, 클래스, 또는 필드가 더 이상 사용되지 않음을 나타냅니다.
 * 이 어노테이션이 붙은 요소는 향후 버전에서 제거될 수 있으므로, 사용을 피해야 합니다.
 */
@Deprecated
public int getInt() {
    return 1;
}

 

오버라이드 어노테이션은 잘못된 오버라이딩을 검사해주어 컴파일 에러를 야기하는 어노테이션이고, 

Deprecated 어노테이션은 개발자에게 사용하지 않는 것을 권장하는 필드나 메서드를 알리는 데에 사용하는 어노테이션입니다. 

메타 어노테이션

어노테이션을 위한 어노테이션으로 어노테이션을 정의할 때 사용 그 예시로 Target, Retention, Documented등이 있습니다.

 

Target은 어노테이션을 정의할 때 적용대상을 지정하는데 사용하는 어노테이션이며 Retention은 어노테이션이 유지되는 기간을 지정하는데 사용하는 어노테이션입니다.

/**
 * {@code @CustomAnnotation}은 메타 어노테이션 {@code @Retention}과 {@code @Target}을 사용한 예제입니다.
 * 이 어노테이션은 클래스에만 적용될 수 있으며, 런타임 동안 유지됩니다.
 */
@Retention(RetentionPolicy.RUNTIME)  // 이 어노테이션은 런타임까지 유지됩니다.
@Target(ElementType.TYPE)           // 이 어노테이션은 클래스, 인터페이스 또는 열거형에만 적용됩니다.
@interface CustomAnnotation {
    String value() default "Default Value";
}

@CustomAnnotation(value = "Hello, Metaannotations!")  // CustomAnnotation을 클래스에 적용
public class MetaAnnotation {
    public void display() {
        System.out.println("Sample class method.");
    }
}

Source는 소스파일에만 존재하는것. Runtime은 클래스파일에만 존재하는것. 실행시에 사용가능하고 Inherited는 어노테이션의 상속에 필요한 어노테이션입니다.

사용자 정의 어노테이션

어노테이션은 커스텀하여 사용할 수도 있는데, 다음과 같이 어노테이션을 생성하고, 사용하면됩니다

만약에 다른 폴더에 존재하는 파일이면 어노테이션을 import 하면 됩니다.

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * {@code @LogExecutionTime} 어노테이션은 메서드의 실행 시간을 측정하고 로그로 출력하는 데 사용됩니다.
 * 이 어노테이션은 메서드에만 적용됩니다.
 */
@Retention(RetentionPolicy.RUNTIME)  // 이 어노테이션은 런타임 동안 유지됩니다.
@Target(ElementType.METHOD)         // 이 어노테이션은 메서드에만 적용됩니다.
public @interface LogExecutionTime {
}


// 다른 폴더
public class Sample {

    /**
     * 이 메서드는 실행 시간을 측정할 대상입니다.
     * {@code @LogExecutionTime} 어노테이션을 통해 실행 시간을 로깅합니다.
     */
    @LogExecutionTime
    public void performTask() {
        try {
            // 임의의 작업 (2초 지연)
            Thread.sleep(2000);
            System.out.println("Task performed.");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

 

특징

  • 적용시 값을 지정하지 않으면, 사용될 수 있는 기본값을 지정할 수 있다.
  • 요소가 하나이고 이름이 value일 때는 요소의 이름 생략가능하다.
  • 요소의 타입이 배열인 경우, 괄호{}를 사용해야 한다.

규칙

  • 요소의 타입은 기본형, String, enum, 어노테이션, Class만 허용된다.
  • 괄호()안에 매개변수를 선언할 수 없다.
  • 예외를 선언할 수 없다.
  • 요소의 타입을 매개변수로 정의할 수 없다.(<T>)

코드

+ Recent posts