자바는 14버전부터 record라는 새로운 기능을 도입하여 데이터 객체(Data Object)를 보다 간결하고 명확하게 정의할 수 있게 되었다. 이번 포스팅에서는 record의 기본 사용법과 특징을 살펴보고, 기존의 DTO(Data Transfer Object)와의 차이점도 함께 알아보겠다.

record란 무엇인가?

record는 불변 객체(immutable object)를 쉽게 생성할 수 있도록 도와주는 자바의 새로운 타입이다. 주로 데이터 전달 객체(Data Transfer Object, DTO)나 값을 담는 컨테이너로 사용된다. record를 사용하면 클래스 작성 시 필수적인 반복 작업들을 자동으로 처리할 수 있다.

기본 사용법

다음은 record를 사용한 간단한 예제이다

public record Person(String name, int age) {}

위의 예제는 Person이라는 레코드를 정의한 것이다. Person 레코드는 두 개의 필드 name과 age를 가진다. 이렇게 정의된 레코드는 다음과 같은 작업들을 자동으로 처리한다.

  1. 필드 정의: String name, int age 필드가 자동으로 정의된다.
  2. 생성자: 모든 필드를 인수로 받는 생성자가 자동으로 생성된다.
  3. 접근자 메서드: name()과 age() 메서드가 자동으로 생성된다.
  4. equals() 및 hashCode(): 필드를 기반으로 한 equals() 및 hashCode() 메서드가 자동으로 생성된다.
  5. toString(): 필드 값을 포함하는 toString() 메서드가 자동으로 생성된다.

사용 예제

public class Main {
    public static void main(String[] args) {
        Person person = new Person("Alice", 30);

        // 자동 생성된 메서드 사용
        System.out.println(person.name()); // 출력: Taeho
        System.out.println(person.age());  // 출력: 32
        System.out.println(person);        // 출력: Person[name=Taeho, age=32]
    }
}

커스터마이징

레코드 클래스는 커스터마이징할 수 있다. 예를 들어, 추가 메서드를 정의하거나 생성자를 커스터마이징할 수 있다.

public record Person(String name, int age) {
    // 추가 메서드
    public String greet() {
        return "Hello, " + name;
    }

    // 커스텀 생성자
    public Person {
        if (age < 0) {
            throw new IllegalArgumentException("Age cannot be negative");
        }
    }
}

위 예제에서는 greet()라는 추가 메서드와 나이가 음수일 경우 예외를 던지는 커스텀 생성자를 정의하였다.

DTO와 record의 차이점

DTO(Data Transfer Object)와 record는 데이터 객체를 정의하는 데 사용되지만, 두 가지 방식에는 몇 가지 차이점이 있다.

  • 보일러플레이트 코드: DTO는 필드 정의, 생성자, getter/setter 메서드, equals(), hashCode(), toString() 메서드를 수동으로 작성해야 한다. 반면 record는 이러한 메서드들을 자동으로 생성하여 보일러플레이트 코드를 크게 줄여준다.
  • 불변성: DTO는 기본적으로 가변 객체(mutable object)로 설계되는 경우가 많다. 그러나 record는 불변 객체로 설계되어, 한 번 생성된 후 변경할 수 없다. 이는 멀티스레드 환경에서 안전성을 보장해준다.
  • 의도 표현: record는 데이터 객체의 의도를 명확하게 표현할 수 있으며, 데이터만 담고 있는 객체임을 쉽게 알 수 있다. 반면, DTO는 일반 클래스와 구별하기 어려울 수 있다.

 

결론

자바의 record는 데이터 중심의 클래스를 쉽게 정의하고 관리할 수 있게 해주는 강력한 도구이다. 이를 통해 코드의 가독성과 유지보수성을 높일 수 있다. DTO와 비교했을 때, record는 보일러플레이트 코드를 줄이고 불변성을 보장하는 등 여러 가지 장점을 제공한다. 앞으로 자바로 데이터 객체를 정의할 때 record를 적극적으로 활용해보자.

이 취약점으로 주말에 잘쉬고 있다가 갑자기 일을 하게 되었다.. 보편적으로 안쓰는 업체가 찾기 힘든 라이브러리에서 크리티컬한 취약점이 발견된 것이다. 이후에도 지속적으로 개발 커뮤니티에도 해당 이슈가 올라와서 나도 내가 아는바에 대한 내용을 기록으로 남겨본다

 

[개요]

자바 기반으로 만들어지고 전세계적으로, 대중적(?)으로 많이 사용되는 로깅을 위한 라이브러리로 log4j가 있다.

이번 이슈가 된 취약점은 log4j가 넘겨받은 변수를 그대로 로깅하는 것이 아니라 해당 변수를 분석해 실행할 수 있는 lookups 기능에서 발견되었다. lookups란 예를들어 log.error("에러 : " + result); 에서 result가 ${env:PASSWORD}로 설정되어 있다면 "에러 : ${env:PASSWORD}"를 그대로 로깅하는 것이 아니라 이에 해당하는 설정값을 찾아 "에러 : qwer1!"를 로깅하게 되는 기능이다. lookup에는 여러가지 기능들이 있는데, 이번 케이스는 JDNI 룩업을 이용한 공격이 가능하다는 취약점이다. 이른바 Log4Shell 취약으로 불린다.

 

[취약점 공격 대상]

log4j-core 버전 2.10.0 이상 ~ 2.15.0 미만

 

[대응방법]

1. log4j를 2.15.0 이상으로 업그레이드 한다. (Java8 이상에서만 기동된다)

2. log4j 2.10.0 이상 사용 시 다음의 방법 중 한 가지 이상의 방법을 사용한다. 개인적인 의견으로는 서비스의 운영환경마다 다르겠지만.. 운영하는 웹서비스가 많아 관리 포인트가 많고 복잡할 경우.. 이 방법은 관리측면에서는 별로 좋은 방법 같진 않다. 개발자가 프로젝트 소스코드 외부에서 관리해야하고(서버/인프라 단), 이 설정이 되어있는 이유에 대해 나중에 시간이 많이 지나서 누가 보더라도 알 수 있도록 히스토리를 잘 관리해야 한다는 점..

  •  
  • Java 실행 인자(Arguments) 에 시스템 속성을 추가한다. -Dlog4j2.formatMsgNoLookups=true
  • Java 실행 계정의 환경 변수 혹은 시스템 변수로 LOG4J_FORMAT_MSG_NO_LOOKUPS=true를 설정한다.

3. log4j 2.7.0 이상 사용 시 log4j 설정(log4j.xml 등)에 PatternLayout 속성에 있는 %m 부분을 %m{nolookups} 으로 교체한다.

4. 만약 log4j 버전이 2.10.0보다 낮다면, 이번 이슈의 공격대상은 아니다.. 그렇지만 1버전 대의 경우는 지원이 중단되었으며, 이미 RCE 보안이슈가 존재하므로 가급적 업그레이드 방안을 모색하는 것이 좋다

 

[팁]

위 대응방법은 기본적인 조치 방법에 대해 설명한 것이고, 이와 더불어 조치 시 아래 오픈소스를 참고하면 빠르게 조치할 수 있다.

https://github.com/logpresso/CVE-2021-44228-Scanner

 

GitHub - logpresso/CVE-2021-44228-Scanner: Vulnerability scanner and mitigation patch for Log4j2 CVE-2021-44228

Vulnerability scanner and mitigation patch for Log4j2 CVE-2021-44228 - GitHub - logpresso/CVE-2021-44228-Scanner: Vulnerability scanner and mitigation patch for Log4j2 CVE-2021-44228

github.com

해당 소프트웨어를 서버로 옮겨서 웹서비스가 설치된 디렉토리를 중심으로 탐색하면 대상 log4j를 사용중인지 확인 할 수 있다. 사용방법은 해당 레포지토리 README에 상세히 나와있고 매우 간편하다.

 

그리고 우리 서비스 외에도 밴더 솔루션등에도 log4j 이슈가 있을 수 있으니 꼭 문의를 넣어서 확인해보자

다른 환경에 있던 스프링 프로젝트를

내 개발환경으로 끌어들이니 메이븐 pom.xml에서 Missing artifact 에러가 발생했다..

한참 이유를 찾아 다녔는데

<type> 엘리먼트에 대한 속성을 지정해주지 않았기 때문이었다.

메이븐을 통해 라이브러리 의존성을 지정해 줄 때,

경우에 따라 어떤 메이븐 라이브러리는 pom에 정의된 dependency를 받아오게끔 구성되어 있다..

이런 라이브러리를 적용할때 <type>pom</type>을 빼먹으면

jar를 찾을 수 없어서 에러가 발생한다.


예시)

<!-- https://mvnrepository.com/artifact/com.github.oshi/oshi-dist -->

<dependency>

    <groupId>com.github.oshi</groupId>

    <artifactId>oshi-dist</artifactId>

    <version>3.4.0</version>

    <type>pom</type>

</dependency>


참고)

https://maven.apache.org/guides/introduction/introduction-to-dependency-mechanism.html

http://knight76.tistory.com/entry/maven-type-pom


http://ande226.tistory.com/101

http://egloos.zum.com/kwaknu/v/4988500

개발자한테 편한 것일수록, 오픈소스일수록 개발환경을 만드는건 더욱 복잡해지고


이해해야 할 양도 많아지는 것 같다.


스프링 프레임워크의 경우도 특히나 그렇다고 생각한다.. (다운받고 설정해주어야 할 것이 정말 다양하다 -.-;)


구글링을 열심히 하던 중 스프링 개발환경을 구성하는데 있어서


절차적으로 정리가 잘되어있는 글이 있어서 여기에 URL을 남겨둔다..


http://addio3305.tistory.com/32

+ Recent posts