- 여러 메서드 호출을 연결하여 호출하는 프로그래밍 기술입니다. 이를 이용하면 코드를 간결하게 작성하고 가독성을 높일 수 있습니다. - 이를 통해 객체의 메서드를 연속적으로 호출하여 작업을 수행할 수 있으며, 각 메서드는 호출된 객체를 반환하여 다음 메서드 호출이 가능하게 합니다. - 이러한 메서드 체이닝은 플루언트 인터페이스(Fluent Interface)나 빌더 패턴(Builder Pattern)을 구현하는 데에 자주 사용되며, 간결하고 읽기 쉬운 코드를 작성하는 데 도움이 됩니다.
💡 기본 구조
- "method1", "method2", "method3" 메서드가 모두 같은 객체 "obj"에 대해 연속적으로 작업을 수행하며, 각 메서드는 연산 후에 그 결과를 반환하는 객체를 반환합니다.
- 메소드 체이닝을 기반한 객체 지향 API 인터페이스 메서드입니다. 소스코드의 가독성을 높이기 위한 목적으로 사용되며 인터페이스 안에 도메인 특화 언어(DSL)를 이용하여 작성합니다. - 각 메서드는 수정된 객체를 반환하여 다음 연산을 계속할 수 있으며, 주로 변경 가능한 객체에서 사용됩니다.
- 람다식(Lambda Expression)를 이용한 기술 중에 하나로 데이터 소스(컬렉션, 배열, 난수, 파일 등…)를 조작 및 가공, 변환하여 원하는 값으로 반환해주는 인터페이스를 의미합니다.
💡 Stream API 활용 예시
- 해당 경우에서는 Optional<String> 객체를 구성하기위해 stream()을 사용하며, 해당 데이터에 값을 정제하기 위해 filter() 메서드와 findFirst() 메서드가 체이닝 되었습니다.
/*
* [CASE2] ArrayList 이용한 방법
*/// 1. ArrayList InitializeList<String> findFirstArrList = newArrayList<>(Arrays.asList("kim", "lee", "park", "lee", "jung", "jin"));
// 2. ArrayList to Optional<String> : j로 시작하는 단어의 첫번째 요소를 반환합니다.Optional<String> findFirstOptionalStr = findFirstArrList.stream().filter(i -> i.startsWith("j")).findFirst();
- JPA에서 제공하는 타입 세이프한 쿼리 생성 기능입니다. 해당 API를 사용하면 JPQL(JPA Query Language)을 직접 작성하지 않고도 쿼리를 작성할 수 있으며, 컴파일 시점에 오류를 확인할 수 있어 안전합니다.
💡 JPA Creteria API 활용 예시
- 해당 예시에서는 SQL문을 메서드 체이닝을 기반으로 구성하는 Creteria API 사용예시입니다. - 테이블과 도메인을 매핑하여 사용되며 query를 구성할때 .select() 메서드로 테이블의 컬럼을 조회하고 .where() 메서드를 통해 조건문을 생성합니다. 이는 메서드 체이닝을 통해 연결이 되어 있습니다.
- 단위 테스트를 위해 모의 객체를 생성하고 관리하는 데 사용되는 Java 오픈소스 프레임워크를 의미합니다.
- 이를 사용하면 실제 객체의 동작을 모방하는 모의 객체(Mock Object)를 생성하여 코드의 ‘특정 부분을 격리’시키고 테스트하기 쉽게 만들어줍니다. -주로 단일 컴포넌트의 동작을 테스트하는 데 사용되며 클래스 내의 개별 메서드나 함수, 서로 다른 클래스 또는 컴포넌트 간의 상호작용, 객체들 간의 협업 등을 테스트할 수 있습니다.
💡 Mockito 활용 예시
- 해당 테스트의 요점은 가상의 모의 객체를 만들어서 service에 처리된 결과를 비교하는 서버스를 테스트하기 위함입니다. 해당 처리를 위해서 메서드 체이닝 형태의 구조로 처리합니다.
1. @ExtendWith(MockitoExtension.class) - JUnit5와 Mockito를 함께 사용하기 위한 어노테이션을 선언합니다.
2. @Mock - 모의 객체로 사용할 sqlSession와 codeMapper를 선언합니다. 3. @InjectMocks - 실제 테스트를 수행할 ServiceImpl로 객체를 생성하고 의존성 주입을 하기 위해 어노테이션으로 선언합니다. 4. @Test - 실제 처리되는 테스트가 수행되는 메서드입니다.
1. 가상의 객체를 구성합니다.
2. Service와 비교할 반환값을 구성합니다.
3. Mapper에 가상의 객체를 넣고 반환되는 값을 expectedCodeList 값으로 지정합니다.
4. 가상의 sqlSession에 Mapper를 지정해 줍니다. (* 해당 부분은 서비스 구현체(ServiceImpl)에서 사용되는 데이터를 구성하였습니다)
5. 테스트 대상 메서드를 호출합니다.
6. 반환된 코드 리스트가 예상한 값과 일치하는지 검증합니다.
/**
* CodeService 구현체를 테스트를 수행합니다.
*
* @author : lee
* @fileName : CodeServiceTest
* @since : 12/11/23
*/@ExtendWith(MockitoExtension.class)classCodeServiceTest {
@Mockprivate SqlSession sqlSession;
@Mockprivate CodeMapper codeMapper;
@InjectMocksprivate CodeServiceImpl codeService;
@BeforeEachvoidsetUp() {
//
}
@Test@DisplayName("객체 값을 기반으로 여러개의 코드 값들을 조회합니다.")voidselectCodeList() {
// *************** given : 사전 조건 ******************// 1. 가상의 객체를 구성합니다.CodeDtomockCodeDto= CodeDto.builder().cd("java").build();
// 2. Service와 비교할 반환값을 구성합니다.
List<CodeDto> expectedCodeList = newArrayList<>();
// *************** when : 동작 검증 ******************// 3. 가상의 codeMapper의 메서드를 호출하여 가상의 객체로 반환되도록 지정합니다.
Mockito.when(codeMapper.selectCodeList(mockCodeDto)).thenReturn(expectedCodeList);
// 4. 가상의 sqlSession에 Mapper를 지정해줍니다. (* 해당 부분은 서비스 구현체(ServiceImpl)에서 사용되는 데이터를 구성하였습니다)
Mockito.when(sqlSession.getMapper(CodeMapper.class)).thenReturn(codeMapper);
// 5. 테스트 대상 메서드를 호출합니다.
List<CodeDto> actualCodeList = codeService.selectCodeList(mockCodeDto);
// *************** when : 결과 검증 ******************// 6. 반환된 코드 리스트가 예상한 값과 일치하는지 검증합니다.
Assertions.assertEquals(expectedCodeList, actualCodeList);
}
}
- 스프링 프레임워크에서 제공하는 웹 애플리케이션 테스트용 라이브러리를 의미합니다. 이를 사용하면 웹 애플리케이션의 다양한 컴포넌트를 테스트할 수 있습니다. - MockMvc를 사용하면 HTTP 요청을 작성하고 컨트롤러의 응답을 검증할 수 있습니다. 이를 통해 통합 테스트를 실행하지 않고도 컨트롤러의 동작을 확인할 수 있습니다.
💡 MockMvc 활용 예시
- MockMvc를 이용한 테스트 작업에서 플루언트 인터페이스 구조를 통하여 구성합니다. - testExample() 메서드를 확인하면 아래와 같이 MockMvc 객체를 생성하여 이를 구성합니다. 구성은 ‘.메서드’ 형태로 메서드 체이닝 방식 구성이 되었습니다.
@ExtendWith(MockitoExtension.class)// Junit5 - Mockito 연동classCodeControllerTest {
private MockMvc mockMvc; // HTTP 호출을 위한 MockMVC 사용@BeforeEachvoidsetUp() {
mockMvc = MockMvcBuilders.standaloneSetup(codeController).build();
}
@TestpublicvoidtestExample()throws Exception {
mockMvc.perform(get("/example")) // "/example"로 GET 요청 수행
.andExpect(status().isOk()) // 상태 코드 200인 성공적인 응답을 기대합니다
.andExpect(content().string("expected")) // 응답 내용이 "expected"와 같을 것으로 기대합니다
.andExpect(jsonPath("$.property").value("expected")) // JSON 속성 "property"의 값이 "expected"와 같을 것으로 기대합니다
.andExpect(view().name("expectedView")) // 뷰 이름이 "expectedView"와 같을 것으로 기대합니다
.andExpect(model().attribute("attributeName", "expectedValue")) // 모델 속성 "attributeName"의 값이 "expectedValue"와 같을 것으로 기대합니다
.andExpect(redirectedUrl("expectedUrl")); // "expectedUrl"로의 리다이렉트를 기대합니다
}
}
💡 빌더 패턴(Builder Pattern) - 객체의 생성 과정과 표현 방법을 분리하여 동일한 생성 절차에서 서로 다른 표현 결과를 만들 수 있게 하는 패턴을 의미합니다. - 생성자의 매개 변수가 많을 때 객체 생성을 더 쉽고 가독성 있게 만드는 디자인 패턴으로 불변성을 가진 객체를 만들때 특히 유용합니다.
- Java에서 생성자 패턴의 종류 중 하나로 사용되는 Builder 패턴은 복합 객체의 생성 과정과 표현 방법을 분리하여 동일한 생성 절차에서 서로 다른 표현 결과를 만들 수 있게 하는 패턴을 의미합니다. - 이러한 생성자를 구성하여 사용할 때 메서드 체이닝으로 사용이 됩니다.
💡 사용예시 -1 : Lombok Builder 선언 및 활용 방법 - Builder 패턴 구성하고 이를 사용할 때 메서드 체이닝이 사용되는 예시입니다. - Builder 패턴에서는 객체를 구성할때 .builder()로 시작하여 .build()로 닫습니다. 이 사이에 해당 값을 채워넣기 위해 .status(), .message(), .code()와 같은 형태의 메서드를 통하여 해당 값들을 채워 객체를 구성합니다.
- 해당 경우에서는 위에서 객체로 값을 구성한 형태라면 이를 특정 값에 대해서만 재구성하기 위한 방법으로 사용되는 경우입니다. - 우선 userDto1라는 객체를 구성하였고 객체에 값을 채웠습니다. 이전의 값을 유지한채 일부 값만 변경하고자 하는 경우는 .toBuilder() 메서드를 이용하여 일부만 수정합니다ㅍ
**
* 객체1을 구성합니다.
*/
UserDtouserDto1= UserDto
.userBuilder()
.userSq(1)
.userNm("Lee")
.userId("adjh54")
.build();
log.debug(userDto1.toString()); // UserDto(userSq=1, userId=adjh54, userPw=null, userNm=Lee, userSt=null)/**
* 구성한 객체1을 기반으로 객체2를 재구성합니다.
* 객체1의 값은 유지하되 변경하고자 하는 값은 객체2로 변경합니다.
*/UserDtouserDto2= userDto1
.toBuilder()
.userNm("Jong")
.build();
log.debug(userDto2.toString()); // UserDto(userSq=1, userId=adjh54, userPw=null, userNm=Jong, userSt=null)
- toBuilder 메소드를 사용하면 원래 객체를 복사하여 새로운 Builder 객체를 생성합니다. 이렇게 생성된 Builder를 이용해 원래 객체의 일부 값을 변경한 상태로 새 객체를 생성할 수 있습니다. - 이 경우, 원래 객체는 불변성을 유지하며 새로운 객체는 원래 객체의 사본이 되는 것입니다. 따라서, toBuilder를 사용하더라도 Builder 패턴의 불변성은 유지됩니다.