웹개발의 봄, Spring 2주차 - 2

45902 단어 JavaSpringJava

2022년 3월 29일(화) ~ 30일(수)
[스파르타코딩클럽] 웹개발의 봄, Spring 2주차 과정 - 2

◎ 스프링의 구조 3가지

  1. Controller : 가장 바깥 부분, 요청/응답을 처리함.
    → API와 통신함
  2. Service : 중간 부분, 실제 중요한 작동이 많이 일어나는 부분
    → Update를 담당
// domain.Course.java Method에 추가

public void update(Course course) {
    this.title = course.title;
    this.tutor = course.tutor;
}

// service.CourseService.java

@RequiredArgsConstructor
@Service // 스프링에게 이 클래스는 서비스임을 명시
public class CourseService {

    // final: 서비스에게 꼭 필요한 녀석임을 명시 / 한번 값이 부여되면 변경될 수 없다.
    private final CourseRepository courseRepository;

      // @RequiredArgsConstructor로 대체
//    // 생성자를 통해, Service 클래스를 만들 때 꼭 Repository를 넣어주도록
//    // 스프링에게 알려줌
//    public CourseService(CourseRepository courseRepository) {
//        this.courseRepository = courseRepository;
//    }

// 위의 두 과정을 통해 CourseRepository 가 내가 언제든 쓸 수 있게 스프링이 생성해서 넘겨줌

    //업데이트 부분
   @Transactional // SQL 쿼리가 일어나야 함을 스프링에게 알려줌 // 업데이트 시 반영하게 함
   // 업데이트 시 필요한 것 : id, 업데이트 할 정보를 가져올 녀석 (title, tutor 정보가 들어있음)
    public Long update(Long id, CourseRequestDto requestDto) {
        Course course1 = courseRepository.findById(id).orElseThrow(
                () -> new IllegalArgumentException("해당 아이디가 존재하지 않습니다.")
        );
        course1.update(requestDto);
        return course1.getId();
    }
}
  1. Repo : 가장 안쪽 부분, DB와 맞닿아 있음.
    → Repository, Entity

◎ JPA 심화

  • CRUD? : Create, Read, Update, Delete
  • Create & Read
	@Bean
    public CommandLineRunner demo(CourseRepository repository) {
        return (args) -> {
            // 데이터 저장하기
            repository.save(new Course("프론트엔드의 꽃, 리액트", "임민영"));

            // 데이터 전부 조회하기
            List<Course> courseList = repository.findAll();
            for (int i = 0; i < courseList.size(); i++) {
                Course course = courseList.get(i);
                System.out.println(course.getId());
                System.out.println(course.getTitle());
                System.out.println(course.getTutor());
            }

            // 데이터 하나 조회하기
            // findById 아이디로 찾기
            // orElseThrow 없을 때 할꺼 작성
            // NullPointerException 가리킬게 없을 때 대처방법
            Course course = repository.findById(2L).orElseThrow(
                    () -> new NullPointerException("아이디가 존재하지 않습니다.")
            );
        };
    }
  • Update
@Bean
public CommandLineRunner demo(CourseRepository courseRepository, CourseService courseService) {
    return (args) -> {
        courseRepository.save(new Course("프론트엔드의 꽃, 리액트", "임민영"));

        System.out.println("데이터 인쇄");
        List<Course> courseList = courseRepository.findAll();
        for (int i=0; i<courseList.size(); i++) {
            Course course = courseList.get(i);
            System.out.println(course.getId());
            System.out.println(course.getTitle());
            System.out.println(course.getTutor());
        }

        CourseRequestDto requestDto = new CourseRequestDto("웹개발의 봄, Spring", "임민영");
        courseService.update(1L, requestDto);
        courseList = courseRepository.findAll();
        for (int i=0; i<courseList.size(); i++) {
            Course course = courseList.get(i);
            System.out.println(course.getId());
            System.out.println(course.getTitle());
            System.out.println(course.getTutor());


       }
    };
}
  • Delete
	courseRepository.deleteAll(); // 삭제하는 부분

◎ Lombok

  • 자바 프로젝트를 진행하는데 거의 필수적으로 필요한 메소드/생성자 등을 자동생성해줌으로써 코드를 절약할 수 있도록 도와주는 라이브러리

  • 설치 : project 생성시 진행했음

  • 환경설정

    • Setting > "Annotation Processors" 입력 > 우측 "Enable annotation processing" 체크하고 OK 클릭
    • Shift 두 번 누르고 plugins 입력 후 엔터 > lombok 설치 후 재시작
  • 사용

    • @Getter : getter 부분 삭제하고 사용
    • @Setter : setter 부분 삭제하고 사용
    • @NoArgsConstructor : 기본생성자를 대신 생성해줌
    • @RequiredArgsConstructor : private final로 선언된 문자를 자동으로 Class에서 넣어줌

◎ DTO(Data Transfer Object)

  • 문제점 : 기존에 있는 Course class를 이용해 업데이트 하는 내용을 전달받음.
    • 내가 아닌 다른 사람이 Course class를 변경을 하면 오류 날 가능성이 높아짐
    • DB가 변경될 가능성이 커짐

-> DB에 연결된 class는 그대로 두고, 물고다니는 class는 따로 만들 가능성이 부각됨

  • 생성하기
// domain.CourseRequestDto.java

@NoArgsConstructor
@Getter
public class CourseRequestDto {
    private String title;
    private String tutor;

    public CourseRequestDto(String title, String tutor) {
        this.title = title;
        this.tutor = tutor;
    }
}
  • 적용하기
// service.CourseService

@RequiredArgsConstructor
@Service // 스프링에게 이 클래스는 서비스임을 명시
public class CourseService {

    // final: 서비스에게 꼭 필요한 녀석임을 명시 / 한번 값이 부여되면 변경될 수 없다.
    private final CourseRepository courseRepository;

    // @RequiredArgsConstructor로 대체
//    // 생성자를 통해, Service 클래스를 만들 때 꼭 Repository를 넣어주도록
//    // 스프링에게 알려줌
//    public CourseService(CourseRepository courseRepository) {
//        this.courseRepository = courseRepository;
//    }

    // 위의 두 과정을 통해 CourseRepository 가 내가 언제든 쓸 수 있게 스프링이 생성해서 넘겨줌

    //업데이트 부분
   @Transactional // SQL 쿼리가 일어나야 함을 스프링에게 알려줌 // 업데이트 시 반영하게 함
   // 업데이트 시 필요한 것 : id, 업데이트 할 정보를 가져올 녀석 (title, tutor 정보가 들어있음)
    public Long update(Long id, CourseRequestDto requestDto) {
        Course course1 = courseRepository.findById(id).orElseThrow(
                () -> new IllegalArgumentException("해당 아이디가 존재하지 않습니다.")
        );
        course1.update(requestDto);
        return course1.getId();
    }
}

// domain.Course.java

@Getter // Getter 코드를 대체
@NoArgsConstructor // 기본생성자를 대신 생성해줍니다.
@Entity // 테이블임을 나타냅니다.
public class Course extends Timestamped{

    @Id // ID 값, Primary Key로 사용하겠다는 뜻입니다.
    @GeneratedValue(strategy = GenerationType.AUTO) // 자동 증가 명령입니다.
    private Long id;
    @Column(nullable = false) // 컬럼 값이고 반드시 값이 존재해야 함을 나타냅니다.
    private String title;
    @Column(nullable = false)
    private String tutor;

    public Course(CourseRequestDto requestDto) {
        this.title = requestDto.getTitle();
        this.tutor = requestDto.getTutor();
    }

    //생성자
   public Course(String title, String tutor) {
        this.title = title;
        this.tutor = tutor;
    }

    // 업데이트트
   public void update(CourseRequestDto requestDto) {
        this.title = requestDto.getTitle();
        this.tutor = requestDto.getTutor();
    }
}

// Application
@EnableJpaAuditing // 생성, 수정 일자에따라 날짜가 자동으로 업데이트
@SpringBootApplication
public class Week02Application {

    public static void main(String[] args) {
        SpringApplication.run(Week02Application.class, args);
    }

    @Bean
    public CommandLineRunner demo(CourseRepository courseRepository, CourseService courseService) {
        return (args) -> {
            courseRepository.save(new Course("프론트엔드의 꽃, 리액트", "임민영"));

            System.out.println("데이터 인쇄");
            List<Course> courseList = courseRepository.findAll();
            for (int i=0; i<courseList.size(); i++) {
                Course course = courseList.get(i);
                System.out.println(course.getId());
                System.out.println(course.getTitle());
                System.out.println(course.getTutor());
            }

            CourseRequestDto requestDto = new CourseRequestDto("웹개발의 봄, Spring", "임민영");
            courseService.update(1L, requestDto);
            courseList = courseRepository.findAll();
            for (int i=0; i<courseList.size(); i++) {
                Course course = courseList.get(i);
                System.out.println(course.getId());
                System.out.println(course.getTitle());
                System.out.println(course.getTutor());
            }

            courseRepository.deleteAll();
        };
    }
}

◎ API

  • API: 클라이언트 - 서버 간의 약속
    클라이언트가 정한대로 서버에게 요청(Request)을 보내면,
    서버가 요구사항을 처리하여 응답(Response)을 반환합니다.

  • REST: 주소에 명사, 요청 방식에 동사를 사용함으로써 의도를 명확히 드러냄을 의미함

  • 주소 요청시 주의 사항

    • 주소에 들어가는 명사들은 복수형을 사용
    • 주소에 동사는 가급적 사용하지 않음
  • ARC(Advanced REST Client) : API 만들고 나서 테스트를 돕는 tool

    • get / delete : 주소를 바로 입력해서 실행.
    • post / put
      • HEADERS에 HTTP request 추가
      • BODY에 json 형식으로 내용 추가하여 실행
  • Controller 코드

// controller.CoursesController.java

@RequiredArgsConstructor
@RestController // json으로 응답하기
public class CourseController {

    private final CourseRepository courseRepository;
    private final CourseService courseService;

    // 어노테이션(Annotation)을 통해해같은 소라도 방식이 다름을 구분합니다.

    // Get 방식
    @GetMapping("/api/courses")
    public List<Course> getCourses() {
        return courseRepository.findAll();
    }

    // Post 방식 
    @PostMapping("/api/courses")
    public Course createCourse(@RequestBody CourseRequestDto requestDto) {
        // @RequestBody:  POST, PUT 방식에서 API에서 넘어오는 데이터를 잘 받으려면 RequestBody의 형태로 받아야함
        
        // requestDto 는, 생성 요청을 의미합니다.
        // 강의 정보를 만들기 위해서는 강의 제목과 튜터 이름이 필요하잖아요?
        // 그 정보를 가져오는 녀석입니다.

        // 저장하는 것은 Dto가 아니라 Course이니, Dto의 정보를 course에 담아야 합니다.
        // 잠시 뒤 새로운 생성자를 만듭니다.
        Course course = new Course(requestDto);

        // JPA를 이용하여 DB에 저장하고, 그 결과를 반환합니다.
        return courseRepository.save(course);
    }
    
    // Put 방식
    @PutMapping("/api/courses/{id}")
    public Long updateCourse(@PathVariable Long id, @RequestBody CourseRequestDto requestDto) {
        return courseService.update(id, requestDto);
    }
    
    // Delete 방식
    @DeleteMapping("/api/courses/{id}")
    public Long deleteCourse(@PathVariable Long id) {
        courseRepository.deleteById(id);
        return id;
    }

좋은 웹페이지 즐겨찾기