1. 빌더 패턴(Builder pattern)이란

객체를 정의하고 생성자를 통해 생성합니다.

Member member = new Member("name", "password", "comment");

이러한 방식은 몇가지 단점이 있기 때문에 객체를 생성하는 별도 builder를 두는 방법이 있다. 이를 Builer Pattern이라고 한다.

Member member = Member.builder()
			.name("name")
			.password("fffewere")
			.comment("hihellor")
			.build();

객체를 생성할 수 있는 빌더를 builder()함수를 통해 얻고 거기에 Setting을 하고 마지막에 build()를 통해 builder를 작동 시켜 객체를 생성한다.

 

2. Builder를 써야하는 이유

 

객체를 생성하는 방법중 builder를 알게 되었습니다.이러한 방법은 왜 쓰일까?

 

  • 생성자 파라미터가 많을 수록 가독성이 좋지 않다.
Member member = new Member("name", "joo1jio", "comment", "abc", "what", "is", "it", "?");

 이처럼 파라미터를 많아지는 상황이 벌어진다면? 이 값들이 어떤걸 의미하는지 이해하기 어렵다.

 이를 Builder Pattern으로 구현하면 각 값을 빌더의 각 값들의 이름이 어떻게 setting 되었는지 바로 알기 쉽다. 

 이러한 경우가 많기 때문에 생성자보다 가독성이 좋은 Builder Pattern을 사용한다.

Member member = Member.builder()
			.name("name")
			.password("fffewere")
			.comment("hihellor")
			.address("seoul")
			.mailbox("40%")
			.build();
  • 어떤 값을 먼저 설정하던 상관없다.

생성자의 경우는 정해진 파라미터 순서대로 각 값을 넣어줘야 하지만 Builder pattern은 빌더의 필드 이름으로 값을 설정하기 때문에

순서에 종속적이지 않다. 내가 편한 순서대로 정하면 된다.

 

public Member(String name, String password, String memo) {
		this.name = name;
		this.password = password;
		this.memo = memo;
}
Member member = Member.builder()
			.password("fffewere")
			.name("name")
			.comment("hihellor")
			.build();

 

2. @Builder

그렇다면 어떻게 구현할까

우선 코드로 Builder를 만들고 그 안에서 멤버 필드별로 값을 설정하고 빌더를 반환하는 함수를 만들면된다.

사실 이렇게 하면은 Getter Setter 처럼 쭉쭉 늘어지는 코드가 많이 생기는것과 비슷하다.

그래서 LOMBOK이 이를 지원한다. Builder Pattern을 적용할 객체에 @Builder를 쓰기만 하면 된다.

 

@Builder
public class Member {
		private String name;
		private String password;
		private String comment;
}

이렇게 되면 위에서처럼 Builder pattern을 써서 객체를 생성할 수 있다.

 

3. @Builder Options

 

 

  • builderMethodName()

builder()를 Rename할 수 있는 기능-> 객체 사용에 조금더 편의를 위해

  • buildMethodName()

build()를 Rename할 수 있는 기능

  • builderClassName()

 클래스 명을  Rename 할 수 있는 기능

Member member = Member.builder()
			.password("fffewere")  //return Memberbuilder
			.name("name")          //return Memberbuilder  
			.comment("hihellor")   //return Memberbuilder
			.build();
  • toBuilder()

default 값이 false  

true로 설정하면 Builder로 만든 인스턴스에서 toBuilder() 메서드를 호출해 그 값을 베이스로 새로운 빌더 패턴을 만들고 새로운 인스턴스가 생성된다.

 

 

 

Member member = Member.builder()
			.name("name")
			.password("fffewere")
			.comment("hihellor")
			.address("seoul")
			.mailbox("40%")
			.build();
            
            
Member member2 = member.toBuilder()
		.password("2131234")
                .address("jeju")
                .build();

 

 

  • access()

builder 클래스의 접근 제어자의 accesslevel의 설정을 할수 있는 기능 

default: public

AccessLevel access() default lombok.AccessLevel.PUBLIC;

 

 

 

@

https://projectlombok.org/features/Builder

절차지향 프로그래밍(Procedural Programming)

 

물이 위에서 아래로 흐르는것처럼 순차적인 처리가 중요하고 프로그램 전체가 유기적으로 연결되도록 만드는 기법

유지보수가 어렵다. 디버깅이 어렵다.

실행순서가 정해져 있으므로 코드 순서가 바뀌면 동일한 결과를 보장할 수 없음

 

 

객체지향 프로그래밍(Object Oriented Programming)

 

실제 세계를 모델링하여 소프트웨어를 개발하는 방법이다.

데이터와 절차를 하나의 덩어리로 묶어서 생각하게 된다. 마치 컴퓨터 부품을 하나씩 사다가 컴퓨터를 조립하는 것과 같다.

상속,다형성,캡슐화

 

데이터 중심 vs 기능 중심

 

관점 지향 프로그래밍(Aspect Oriented Programming)

AOP가 필요한 상황이 어디일까?

- 모든 메소드의 호출 시간을 측정하고 싶다면?

-공통 관심 사항(cross-cutting concern) vs 핵심 관심 사항(core concern)

-회원 가입 시간, 회원 조회 시간을 측정하고싶다.

 

@RequiredArgsConstructor
@Service
public class PersonService {

    private  final PersonRepository personRepository;

    @Transactional
    public Long updatePersonService(Long id, PersonRequestDto personRequestDto){
        long start = System.currentTimeMillis();
try {
    Optional<Person> person = personRepository.findById(id);
    person.update(personRequestDto);
    return id;
}
finally {


    long finish = System.currentTimeMillis();
    long timeMs = finish - start;

    System.out.println("updateperson" + timeMs + "ms");

            
}

문제

-하나하나의 로직마다 측정 시간 로직을 추가해야한다.

-시간을 측정하는 기능은 핵심 관심 사항이 아니다.

-시간을 측정하는 로직은 공통 관심 사항이다.

-시간을 측정하는 로직과 핵심 비즈니스의 로직이 섞여서 유지보수가 어렵다.

-시간을 측정하는 로직을 별도의 공통 로직으로 만들기 매우 어렵다.

-시간을 측정하는 로직을 변경할 때 모든 로직을 찾아가면서 변경해야 한다.

내가 원하는 곳에다가 적용시키자

 

  @Component
  @Aspect //AOP 적용 annotation
  public class TimeTraceAop {
      @Around("execution(*tanibourne.pracspring..*(..))") //타겟팅( )
      public Object execute(ProceedingJoinPoint joinPoint) throws Throwable {
          long start = System.currentTimeMillis();
          System.out.println("START: " + joinPoint.toString());// 어떤 메서드인지 이름을 얻을수 있음
          try {
              return joinPoint.proceed(); // 다음 메서드로 진행
          } finally {
              long finish = System.currentTimeMillis();
              long timeMs = finish - start;
              System.out.println("END: " + joinPoint.toString()+ " " + timeMs +"ms");
          }
          }
          }

 

테스트 코드의 장단점

  1. 장점
    • 예상 동작과 실제 동작을 빠르고 정확하게 테스트 가능하다
    • 테스트의 자동화가 가능하다
    • 리팩토링 후 기존 동작에 문제가 없는지 보장해 준다.
  2. 단점
    • 테스트 코드 작성 시간이 추가되어 개발 시간이 오래 걸림
    • 코드가 변경되면 테스트 코드도 이에 맞추어 변경해야 하므로 테스트코드를 유지보수하는 비용이 추가된다.

 

단위 테스트란

단위 테스트는 하나의 모듈을 기준으로 독립적으로 진행되는 가장 작은 단위의 테스트이다. 모듈이란 애플리케이션에서 작동하는 하나의 기능 또는 메소드이다. 일반적으로 실무에서 테스트 코드는 단위 테스트를 의미한다.

단위 테스트 작성의 필요성

  • 문제 발생 시 어느 부분이 잘못되었는지를 빠르게 확인할 수 있다.
  • 프로그램을 작은 단위로 쪼개 각 단위가 정확하게 동작하는지 확인 할 수있다.
  • 리펙토링하여도 빠르게 문제 여부를 파악해 안정성을 확보할 수 있다.
  • 테스트에 대한 시간과 비용을 절약할 수 있다.

단위 테스트의 특징

일반적으로 요구 사항은 계속해서 변하므로 이에 따라 코드 및 테스트 코드도 변경되어야 한다. 따라서 테스트 코드는 가독성이 있게 작성하는 것을 기본으로 해야 한다.

  • 1개의 테스트 함수에 대해 assert를 최소화한다.
  • 1개의 테스트 함수는 1가지 개념만 테스트 한다.

좋고 깨끗한 테스트 코드는 FIRST라는 규칙을 따라야 한다.

  • Fast : 테스트는 빠르게 동작하여 자주 돌릴 수 있어야 한다.
  • Independent : 각각의 테스트느독립적이며 서로 의존해서는 안된다.
  • Repeatabele : 어느 환경에서도 반복 가능해야 한다.
  • Self-Validationg : 테스트는 bool 값으로 결과를 내어 자체적으로 검증되어야 한다.
  • Timely : 실제 코드를 구현하기 직전에 구현해야한다.

 

개발한 기능을 실행해서 테스트 할 때 자바의 main 메서드를 통해서 실행하거나,웹 애플리케이션의 컨트롤러를 통해서 해당 기능을 실행한다.

이러한 방법은 준비하고 실행하는데 오래 걸리고, 반복 실행하기 어렵고 여러 테스트를 한번에 실행하기 어렵다는 단점이 있다.

자바는 JUnit이라는 프레임워크로 테스트를 실행해서 이러한 문제를 해결한다.

 

통합 테스트란

일반적으로 애플리케이션은 여러 개의 모듈들로 구성이 되고, 모듈들끼리 메세지를 주고 받으면서 기능을 수행한다. 따라서 통합 테스트는 두 개 이상의 모듈이 연결된 상태에서 테스트하는 것을 말한다.

통합 테스트의 특징

  • 모듈 간의 연결에서 발생하는 에러를 검증 가능하다
  • 단위 테스트보다 많은 코드를 테스트하므로 문제 발생 위치를 찾는 것이 어렵다.
  • 여러 모듈이 동시 다발적으로 테스트를 수행하므로 일반적으로 교육받은 전문 테스터와 함께 하게된다.

E2E(end to end) 테스트란

테스트를 사용자 관점에서 테스트하는 방법으로 실제 사용자의 실행 환경과 거의 동일한 환경에서 테스트를 진행하는 것을 말한다. Endpoint 간 테스트로 사용자의 입장에서 사용자가 사용하는 상황을 가정한다.
예를 들면 버튼을 클릭할시 올바른 동작을 수행하는지 url이 제대로 나타나는지 등의 테스트이다.

E2E 테스트의 특징

  • 일반적으로 웹이나 어플에서 GUI를 통해 테스트를 수행한다.
  • 사용자가 직접적으로 사용하는 부분을 점검한다.
  • 사용자의 관점에서 테스트가 가능하다.
  • 다양한 앱의 의존관계가 정확이 작동하는지 확인 할 수 있다.

 

 

 

테스트 라이브러리

  • spring-boot-starter-test
  •  junit: 테스트 프레임워크
    - mockito: 목 라이브러리
    -assertj: 테스트 코드를 좀 더 편하게 작성하게 도와주는 라이브러리

    spring-test: 스프링 통합 테스트 지원

 

 

+ Recent posts