Daily Coding - 22번
sort 사용 안하고 오름차순으로 정렬하시오.
(버블 정렬 알고리즘 참고)
public class BubbleSort {
public int[] bubbleSort(int[] arr) { // sort 사용 안하고 오름차순으로 정렬하는 메서드
int[] result = arr.clone();
for(int j=0; j<arr.length; j++) {
for (int i = 0; i < arr.length - 1 - j; i++) {
if (result[i] > result[i + 1]) { //앞요소가 뒷요소보다 크면 // 부등호만 바꾸면 내림차순 됨
int small = result[i + 1];
result[i + 1] = result[i]; // 두 위치 바뀌기
result[i] = small;
}
}
}
return result;
}
}
시간복잡도가 너무 크다.
줄여보자
public int[] bubbleSort(int[] arr) { // sort 사용 안하고 오름차순으로 정렬하는 메서드
int[] result = arr.clone();
for(int j=0; j<arr.length; j++) {
boolean stillNotSort = false;
for (int i = 0; i < arr.length - 1 - j; i++) {
if (result[i] > result[i + 1]) { //앞요소가 뒷요소보다 크면 // 부등호만 바꾸면 내림차순 됨
int small = result[i + 1];
result[i + 1] = result[i]; // 두 위치 바뀌기
result[i] = small;
stillNotSort =true;
}
}
if(!stillNotSort) break;
}
return result;
}
어떤 요소도 위치가 바뀌지 않는 경우, 배열이 정렬된 상태이기 때문에
boolean을 이용해서 필요없는 수행을 중단시키도록 했다.
서비스 계층
- API 계층에서 전달 받은 클라이언트의 요청 데이터를 기반으로 실질적인 비즈니스 요구사항을 처리하는 계층
API 계층에서 구현한 Controller 클래스가 서비스 계층의 Service 클래스와 메서드 호출을 통해 상호 작용한다는 것
Service
- 도메인 업무 영역을 구현하는 비즈니스 로직을 처리하는 것
도메인 엔티티(Entity) 클래스
- 서비스 계층에서 데이터 액세스 계층과 연동하면서 비즈니스 로직을 처리하기 위해 필요한 데이터를 담는 역할을 하는 클래스
- API 계층에서 전달 받은 요청 데이터를 기반으로 서비스 계층에서 비즈니스 로직을 처리하기 위해 필요한 데이터를 데이터 액세스 계층으로부터 전달 받고, 비즈니스 로직을 처리한 후에는 결과 값을 다시 API 계층으로 리턴해주는 역할
- API계층에서 DTO클래스 같은 느낌
@Getter
, @Setter
@AllArgsConstructor
@NoArgsConstructor
@RestController
@Service
- Controller 클래스에
@RestController
애너테이션을 추가하면 Spring Bean으로 등록된다.- Service 클래스에
@Service
애너테이션을 추가하면 Spring Bean으로 등록된다.- 생성자 방식의 의존성주입(DI)은 생성자가 하나일 경우에는
@Autowired
애너테이션을 추가하지 않아도 DI가 적용된다.
Member 클래스
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@Getter // getter메서드 대신 만들어 줌
@Setter // setter메서드 대신 만들어 줌
@NoArgsConstructor // 파라미터가 없는 기본 생성자를 자동으로 생성
@AllArgsConstructor // (모든 멤버 변수를 파라미터로 갖는) 생성자를 자동으로 생성
public class Member {
private long memberId;
private String email;
private String name;
private String phone;
}
MemberService 클래스
import org.springframework.stereotype.Service;
import java.util.List;
@Service // Spring Bean 등록
public class MemberService {
public Member createMember(Member member){ // 아직 DB연결 안했으니 대충 쓴거
Member createMember = member;
return createMember;
}
public Member updateMember(Member member) { // 아직 DB연결 안했으니 대충 쓴거
Member updatedMember = member;
return updatedMember;
}
public Member findMember(long memberId) { // 아직 DB연결 안했으니 대충 쓴거
Member member = new Member(memberId, "hgd@gmail.com", "홍길동", "010-1234-5678");
return member;
}
public List<Member> findMembers() { // 아직 DB연결 안했으니 대충 쓴거
List<Member> members = List.of(
new Member(1, "hgd@gmail.com", "홍길동", "010-1234-5678"),
new Member(2, "lml@gmail.com", "이몽룡", "010-1111-2222")
);
return members;
}
public void deleteMember(long memberId) {
}
}
MemberController 클래스
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
import javax.validation.constraints.Positive;
import java.util.List;
// 현재 상태로는 2가지 문제점이 있다.
// MemberController의 핸들러 메서드가 DTO 클래스를 엔티티(Entity) 클래스로 변환하는 작업까지 도맡아서 하고 있다.
// 엔티티(Entity) 클래스의 객체를 클라이언트의 응답으로 전송함으로써 계층 간의 역할 분리가 이루어지지 않았다.
@RestController // Spring Bean 자동등록 기능 포함
@RequestMapping("/v1/members")
@Validated // @PathVariable이 추가된 변수에 유효성 검증하려면 붙여야함
//클라이언트의 요청과 클라이언트 요청을 처리하는 핸들러 메서드(Handler Method)를 매핑해준다.
public class MemberController { // 회원 관리를 위한 클래스
private final MemberService memberService;
public MemberController(MemberService memberService) { // 느슨한결합 // Spring이 애플리케이션 로드시 자동으로 주입해줌
this.memberService = memberService;
}
@PostMapping // 클라이언트의 요청 데이터(request body)를 서버에 생성할 때 사용하는 애너테이션
public ResponseEntity postMember(@Valid @RequestBody MemberPostDto memberPostDto){
Member member = new Member();
member.setEmail(memberPostDto.getEmail());
member.setName(memberPostDto.getName());
member.setPhone(memberPostDto.getPhone());
Member response = memberService.createMember(member);
return new ResponseEntity<>(response, HttpStatus.CREATED); // Map 객체를 리턴하게 되면 JSON 형식으로 자동 변환
// ResponseEntity 객체로 데이터를 래핑함으로써 HTTP 응답 상태를 함께 전달
}
@PatchMapping("/{member-id}")
public ResponseEntity patchMember(@PathVariable("member-id") @Positive long memberId, // @Min(1) = 1 이상의 숫자일 경우에만 유효성 검증 통과
@Valid @RequestBody MemberPatchDto memberPatchDto) {
memberPatchDto.setMemberId(memberId);
Member member = new Member();
member.setMemberId(memberPatchDto.getMemberId());
member.setName(memberPatchDto.getName());
member.setPhone(memberPatchDto.getPhone());
Member response = memberService.updateMember(member);
return new ResponseEntity<>(response, HttpStatus.OK);
}
@GetMapping("/{member-id}") // 클라이언트가 서버에 리소스를 조회할 때 사용하는 애너테이션
public ResponseEntity getMember(@PathVariable("member-id") @Positive long memberId){ // 특정 회원의 정보를 클라이언트 쪽에 제공하는 핸들러 메서드
Member response = memberService.findMember(memberId);
System.out.println("# get Member");
return new ResponseEntity<>(response,HttpStatus.OK);
}
@GetMapping
public ResponseEntity getMembers() { // 모든회원 정보 목록을 클라이언트에게 제공하는 핸들러 메서드
List<Member> response = memberService.findMembers();
System.out.println("# get Members List");
return new ResponseEntity<>(response,HttpStatus.OK);
}
@DeleteMapping("/{member-id}")
public ResponseEntity deleteMember(@PathVariable("member-id") @Positive long memberId) {
memberService.deleteMember(memberId);
System.out.println("# delete member");
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
}
}
현재 상태로는 2가지 문제점이 있다.
DTO 클래스와 엔티티(Entity) 클래스를 서로 변환해주는 매퍼(Mapper)를 만들어서 해결 가능하다.
Mapper적용 전
- DTO 클래스-> MemberController -> 엔티티(Entity)
- 엔티티(Entity) -> 응답
Mapper적용
- DTO 클래스-> MemberMapper -> 엔티티(Entity)
- 엔티티(Entity) -> MemberMapper -> DTO 클래스 -> 응답
MemberResponseDto 클래스
import lombok.AllArgsConstructor;
import lombok.Getter;
@Getter
@AllArgsConstructor
public class MemberResponseDto { // 응답 데이터의 역할을 해주는 DTO 클래스
private long memberId;
private String email;
private String name;
private String phone;
}
MemberMapper 클래스
import org.springframework.stereotype.Component;
@Component // 빈 등록
public class MemberMapper {
public Member memberPostDtoToMember(MemberPostDto memberPostDto) { // MemberPostDto를 Member로 변환
return new Member(0L,
memberPostDto.getEmail(),
memberPostDto.getName(),
memberPostDto.getPhone());
}
public Member memberPatchDtoToMember(MemberPatchDto memberPatchDto) { // MemberPatchDto를 Member로 변환
return new Member(memberPatchDto.getMemberId(),
null, // MemberPatchDto객체에는 email 없으니까 null (이메일 수정 안되게 해뒀음)
memberPatchDto.getName(),
memberPatchDto.getPhone());
}
public MemberResponseDto memberToMemberResponseDto(Member member) { // Member를 MemberResponseDto로 변환
return new MemberResponseDto(member.getMemberId(),
member.getEmail(),
member.getName(),
member.getPhone());
}
}
MemberController 클래스
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
import javax.validation.constraints.Positive;
import java.util.List;
import java.util.stream.Collectors;
// 이전버전 상태로는 2가지 문제점이 있다.
// MemberController의 핸들러 메서드가 DTO 클래스를 엔티티(Entity) 클래스로 변환하는 작업까지 도맡아서 하고 있다.
// DTO 클래스-> MemberController -> 엔티티(Entity)
// 엔티티(Entity) 클래스의 객체를 클라이언트의 응답으로 전송함으로써 계층 간의 역할 분리가 이루어지지 않았다.
// 엔티티(Entity) -> 응답
// 현재 상태는 Mapper 클래스를 이용해 위 문제점을 해결했다.
// DTO 클래스-> MemberMapper -> 엔티티(Entity)
// 엔티티(Entity) -> MemberMapper -> DTO 클래스 -> 응답
@RestController // Spring Bean 자동등록 기능 포함
@RequestMapping("/v1/members")
@Validated // @PathVariable이 추가된 변수에 유효성 검증하려면 붙여야함
//클라이언트의 요청과 클라이언트 요청을 처리하는 핸들러 메서드(Handler Method)를 매핑해준다.
public class MemberController { // 회원 관리를 위한 클래스
private final MemberService memberService;
private final MemberMapper memberMapper;
public MemberController(MemberService memberService, MemberMapper memberMapper) { // 느슨한결합 // Spring이 애플리케이션 로드시 자동으로 주입해줌
this.memberService = memberService;
this.memberMapper = memberMapper;
}
@PostMapping // 클라이언트의 요청 데이터(request body)를 서버에 생성할 때 사용하는 애너테이션
public ResponseEntity postMember(@Valid @RequestBody MemberPostDto memberPostDto){
Member member = memberMapper.memberPostDtoToMember(memberPostDto);
Member response = memberService.createMember(member);
return new ResponseEntity<>(memberMapper.memberToMemberResponseDto(response),
HttpStatus.CREATED);
}
@PatchMapping("/{member-id}")
public ResponseEntity patchMember(@PathVariable("member-id") @Positive long memberId,
@Valid @RequestBody MemberPatchDto memberPatchDto) {
memberPatchDto.setMemberId(memberId);
Member member = memberMapper.memberPatchDtoToMember(memberPatchDto);
Member response = memberService.updateMember(member);
return new ResponseEntity<>(memberMapper.memberToMemberResponseDto(response), HttpStatus.OK);
}
@GetMapping("/{member-id}") // 클라이언트가 서버에 리소스를 조회할 때 사용하는 애너테이션
public ResponseEntity getMember(@PathVariable("member-id") @Positive long memberId){ // 특정 회원의 정보를 클라이언트 쪽에 제공하는 핸들러 메서드
Member response = memberService.findMember(memberId);
System.out.println("# get Member");
return new ResponseEntity<>(memberMapper.memberToMemberResponseDto(response),HttpStatus.OK);
}
@GetMapping
public ResponseEntity getMembers() { // 모든회원 정보 목록을 클라이언트에게 제공하는 핸들러 메서드
List<Member> members = memberService.findMembers();
List<MemberResponseDto> response = members.stream()
.map(member -> memberMapper.memberToMemberResponseDto(member))
.collect(Collectors.toList());
System.out.println("# get Members List");
return new ResponseEntity<>(response,HttpStatus.OK);
}
@DeleteMapping("/{member-id}")
public ResponseEntity deleteMember(@PathVariable("member-id") @Positive long memberId) {
memberService.deleteMember(memberId);
System.out.println("# delete member");
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
}
}
List 타입변환을 잘못해서 오류가 났었는데 stream 메서드 import 잘못한줄 알고 헛짓 좀 했다.
기본인데... 타입 생각 항상 하자.
MapStruct
- 매퍼(Mapper) 클래스를 자동으로 구현
MapStruct 기반의 매퍼(Mapper)를 자동 생성하기 위해서 아래와 같이 build.gradle
파일에 MapStruct 의존 라이브러리 추가를 해줘야 한다.
dependencies {
...
...
implementation 'org.mapstruct:mapstruct:1.4.2.Final'
annotationProcessor 'org.mapstruct:mapstruct-processor:1.4.2.Final'
}
@Mapper
@Mapper(componentModel = "spring")
매퍼(Mapper) 인터페이스
- MapStruct 기반의 매퍼(Mapper) 인터페이스 정의한거
package com.codestates.section3week1.member.mapstruct.mapper;
import com.codestates.section3week1.member.Member;
import com.codestates.section3week1.member.MemberPatchDto;
import com.codestates.section3week1.member.MemberPostDto;
import com.codestates.section3week1.member.MemberResponseDto;
import org.mapstruct.Mapper;
@Mapper(componentModel = "spring")
public interface MemberMapper {
Member memberPostDtoToMember(MemberPostDto memberPostDto); // MemberPostDto를 Member로 변환
Member memberPatchDtoToMember(MemberPatchDto memberPatchDto); // MemberPatchDto를 Member로 변환
MemberResponseDto memberToMemberResponseDto(Member member); // Member를 MemberResponseDto로 변환
}
위 과정을 한번 더 반복해서 최대한 참고하지 않고 coffee패키지에 적용해 봤다.