Basic CRUD API 세션

박영준·2022년 11월 30일
0

Java

목록 보기
13/110

전체적인 패키지 및 파일 생성

API 설계하기 (CRUD)

1. CourseController

//Client <--DTO--> Controller <--DTO--> Service <--DTO--> Repository <--Domain(Entity Class)--> DB
//controller의 역할: HTTP로 들어오는 요청을 받고, 받은 데이터를 service로 넘겨주고, service에서 로직을 수행하고 다시 넘어오는 데이터를 받아서 Client 쪽으로 반환
package com.example.basic_crud_10.controller;

import com.example.basic_crud_10.dto.CourseListResponseDto;
import com.example.basic_crud_10.dto.CourseRequestDto;
import com.example.basic_crud_10.dto.CourseResponseDto;
import com.example.basic_crud_10.dto.ResponseDto;
import com.example.basic_crud_10.service.CourseService;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;

@RestController     //JSON 데이터 타입으로 response 해줄것이므로 -> 이걸 달아주지 않으면, 각각의 API에 responsebody를 달아줘야하는 번거로움
@RequestMapping("/api")     //각 API에서 각각의 url에 /api가 공통적으로 반복되므로, 이 반복을 줄여줌
@RequiredArgsConstructor    //데이터베이스와 연결 --> service에 데이터를 넘겨줘야하므로

public class CourseController {
    private final CourseService courseService;  //service와 연결 --> service에 데이터를 넘겨줘야하므로

    //강의 추가
    @PostMapping("/save/course")
    //데이터가 body에서 넘어올때, 이걸 객체로 받아주는 기능
    //ResponseDto: 반환 타입 --> dto 패키지 > ResponseDto.java에 만들기, saveCourse: 메소드 명(원하는 대로 짓기)
    //@RequestBody:Post안에 있는 HTTP Method 안의 body값을 Mapping(키(key) 역할을 하는 데이터와 값(value) 역할을 하는 데이터를 짝 지어(=연결 지어) 저장하는 데이터 구조) 해야하므로
    //CourseRequestDto: JSON 타입으로 넘어오는 데이터를 받아줄 수 있는 객체 --> dto 패키지 > CourseRequestDto.java에 만들기
    public ResponseDto saveCourse(@RequestBody CourseRequestDto requestDto) {
            //데이터를 받아주는 부분
            return courseService.saveCourse(requestDto);    //Client에서 받아온 데이터가 service 쪽으로 넘김(return), saveCourse: 메소드 명(원하는 대로 짓기)
    }


    //강의 리스트 조회(전체 조회)
    @GetMapping("/get/courses")
    //CourseListResponseDto: 반환 타입 --> CourseListResponseDto.java에서. getCourses: 이름
    //List 타입을 이용해서, List<> 이런 식으로 사용할 수 있지만, 여기선 다양한 보기를 위해 이 방식을 사용해봤음
    //(): 모든 값을 뽑아올 것이므로, 따로 조건은 필요없어서 비워둠
    //courseService: courseService와 연결
    public CourseListResponseDto getCourses() {
        return courseService.getCourses();
    }

    //개별 강의 조회
    @GetMapping("/get/course")
    public CourseResponseDto getCourse(@RequestParam Long id) {
        return courseService.getCourse(id);
    }

    //강의 수정
    @PutMapping("/update/course/{id}")
    //@PathVariable Long id: {id}에 넘어오는 값을 받아올 수 있음 --> 수정해야할 id 값
    //@RequestBody CourseRequestDto requestDto: 수정할 내용. @RequestBody: body에 있는 부분이 넘어오므로
    public CourseResponseDto updateCourse(@PathVariable Long id, @RequestBody CourseRequestDto requestDto) {
        //받아온 값들을 넘겨주기
        return courseService.updateCourse(id, requestDto);
    }

    //강의 삭제
    @DeleteMapping("/delete/course/{id}")
    //@PathVariable Long id: 값을 가져오기
    public ResponseDto deleteCourse(@PathVariable Long id){
        return courseService.deleteCourse(id);
    }
}

2. CourseService

//Client <--DTO--> Controller <--DTO--> Service <--DTO--> Repository <--Domain(Entity Class)--> DB
//실제롤 로직을 수행하는 부분
package com.example.basic_crud_10.service;

import com.example.basic_crud_10.dto.CourseListResponseDto;
import com.example.basic_crud_10.dto.CourseRequestDto;
import com.example.basic_crud_10.dto.CourseResponseDto;
import com.example.basic_crud_10.dto.ResponseDto;
import com.example.basic_crud_10.entity.Course;
import com.example.basic_crud_10.repository.CourseRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

@Service
@RequiredArgsConstructor    //데이터베이스와 연결
public class CourseService {
    private final CourseRepository courseRepository;    //데이터베이스와 연결(소통하도록). 즉, courseRepository.java 와 연결

    //강의 추가
    @Transactional
    //Client에서 service로 넘어온 데이터. requestDto: 여기엔 우리가 저장해야할 데이터들이 이미 들어가 있는 상태
    public ResponseDto saveCourse(CourseRequestDto requestDto) {
        //Client에서 받아온 데이터를 저장(save). new Course(requestDto): 데이터 저장을 위해 Course라는 entity를 new로 만들었는데, new이기에 안은 텅 빈 상태 --> Couse.java에서 생상자를 이용해여(예시. getTitle()) 데이터를 세팅함
        //원래: Course course = new Course(requestDto) --> 이 2줄을 한 줄로 줄인 것!
        //     courseRepository.save(course); --> course 자리에 new Course(requestDto)가 왔을 뿐!
        courseRepository.save(new Course(requestDto));
        //ResponseDto: ResponseDto 타입으로 변환함 --> controller에서도 ResponseDto 타입으로 변환해서 반환하므로
        return new ResponseDto("강의 등록 성공", HttpStatus.OK.value());  //ResponseDto.java에서 만들어둔 생성자처럼 순서대로 (String msg, int statusCode) 이렇게 넣으면 객체가 만들어짐
    }


    //강의 리스트 조회(전체 조회)
    @Transactional(readOnly = true)
    public CourseListResponseDto getCourses() {
        //가져온 데이터를 ResponseDto에 넣어주기위해, 값을 넣을 객체 courseListResponseDto 를 만든다
        CourseListResponseDto courseListResponseDto = new CourseListResponseDto();

        //List<Course>: 반환 타입은 List. courseRepository: courseRepository와 연결. findAll(): 모든 값
        List<Course> courses =  courseRepository.findAll();
        for (Course course : courses) {
            //courses를 CourseResponseDto로 바꿔주고, 또 이것을 courseListResponseDto에 넣어준다
            courseListResponseDto.addCourse(new CourseResponseDto(course));
        }
        return courseListResponseDto;
    }


    @Transactional(readOnly = true)
    public CourseResponseDto getCourse(Long id) {
        Course course = checkCourse(id);
        return new CourseResponseDto(course);
    }

    //강의 수정
    @Transactional
    public CourseResponseDto updateCourse(Long id, CourseRequestDto responseDto) {
        //먼저, 데이터베이스 안에 들어있는 값을 가져오기위해, 찾기 --> 그래야, 수정 가능
        Course course = checkCourse(id);
        //responseDto: 업데이트 할 내용 넣는 곳
        course.update(responseDto);
        //course를 수정하고, 반환
        return new CourseResponseDto(course);
    }

    //강의 삭제
    @Transactional
    public ResponseDto deleteCourse(Long id) {
        Course course = checkCourse(id);
        courseRepository.delete(course);
        return new ResponseDto("강의 삭제 성공", HttpStatus.OK.value());
    }

    //개별 강의 조회
    private Course checkCourse(Long id) {
        return courseRepository.findById(id).orElseThrow(
                () -> new RuntimeException("강의를 찾을 수 없다")
        );
    }
}

3. Course

//Client <--DTO--> Controller <--DTO--> Service <--DTO--> Repository <--Domain(Entity Class)--> DB
package com.example.basic_crud_10.entity;

import com.example.basic_crud_10.dto.CourseRequestDto;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Entity     //@Entity 를 통해, 데이터베이스의 creat문을 날린다(데이터를 보낸다?)
@Getter
@NoArgsConstructor

public class Course {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY) //JPA(Java Persisitence API)에서는 전략(strategy)을 정할 수 있다. AUTO, IDENTITY...등 있는데, 이 들의 차이는 구글링해보기!

    private Long id;    //구분을 해줘야하는 PK는 ID이다. id는 따로 주지 않아도 자동으로 생성됨

    //강의 추가
        //강의 추가 API에서 전체 값이 title, instructor, cost
    private String title;   //강의 이름
    private String instructor;  //강사
    private Double cost;    //가격

    //생성자
    public Course(CourseRequestDto requestDto) {    //client에서 받아온 title, instructor, cost 넣기
        this.title = requestDto.getTitle();
        this.instructor = requestDto.getInstructor();
        this.cost = requestDto.getCost();
    }

    //강의 수정
    //데이터베이스에 들어있는 값을 수정
    public void update(CourseRequestDto responseDto) {
        this.title = responseDto.getTitle();
        this.cost = responseDtoresponseDto.getCost();
    }
}

4. CourseRepository

//Client <--DTO--> Controller <--DTO--> Service <--DTO--> Repository <--Domain(Entity Class)--> DB
//Repository : 데이터베이스와 연결되어, 데이터를 저장하는 부분 --> 소통 창구
//인터페이스로 만들기
package com.example.basic_crud_10.repository;

import com.example.basic_crud_10.entity.Course;
import org.springframework.data.jpa.repository.JpaRepository;

//어느 테이블이랑 연결할지 정하는 부분
public interface CourseRepository extends JpaRepository<Course, Long> {     //Course: 연결할 테이블 객체 명, 클래스 명. Long: id값의 타입?
}                                                                               //주의! Course 대신 dto 넣으면 안됨

5. CourseRequestDto

//Client <--DTO--> Controller <--DTO--> Service <--DTO--> Repository <--Domain(Entity Class)--> DB
//메시지를 받아주는 부분
package com.example.basic_crud_10.dto;

import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

@Getter
@Setter     //쭉 코드를 따로 적어줘야하는 것을 줄여줌. Setter가 자동 생성함
@NoArgsConstructor

//강의 추가
public class CourseRequestDto {     //메시지를 받아주는 부분이 title, instructor, cost
    private String title;
    private String instructor;
    private Double cost;
}

6. ResponseDto

//Client <--DTO--> Controller <--DTO--> Service <--DTO--> Repository <--Domain(Entity Class)--> DB
//메시지를 보내는 부분
package com.example.basic_crud_10.dto;

import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@NoArgsConstructor

//강의 추가
public class ResponseDto {
    private String msg;
    private int statusCode;

    //셍성자
    public ResponseDto(String msg, int statusCode) {
        this.msg = msg;
        this.statusCode = statusCode;
    }
}

7. CourseResponseDto

//강의 리스트 조회(전체 조회)
//개별 강의 조회
package com.example.basic_crud_10.dto;

import com.example.basic_crud_10.entity.Course;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@NoArgsConstructor
public class CourseResponseDto {

    //내보낼 데이터는 title, instructor, cost
    private String title;
    private String instructor;
    private Double cost;

    public CourseResponseDto(Course course) {
        this.title = course.getTitle();
        this.instructor = course.getInstructor();
        this.cost = course.getCost();
    }
}

8. CourseListResponseDto

//강의 리스트 조회(전체 조회)
package com.example.basic_crud_10.dto;

import lombok.Getter;
import org.springframework.http.HttpStatus;

import java.util.ArrayList;
import java.util.List;

@Getter
//msg, statusCode를 한번에 같이 넘겨줘야함 --> 그래서, ResponseDto.java의 msg, statusCode를 이용하여, ResponseDto를 상속받도록 함
public class CourseListResponseDto extends ResponseDto{

    //List 형식을 사용해서 넣기. <Course>만 넣어도 무방하나, 우린 변환하는 부분을 이미 만들었기에 <CourseResponseDto>를 사용해보자
    //List<CourseResponseDto>: List 형식으로 변환된 데이터. courseList: 변수명
    //new ArrayList<>(): List<CourseResponseDto> courseList를 new로 만들었을때, 초기화되어 텅 빈 상태로 만들어줘야함 --> 그래야 변수 courseList 안에 값을 넣을 수 있음
    List<CourseResponseDto> courseList = new ArrayList<>();

    //생성자
    //CourseListResponseDto(): 기본 생성자
    //CourseListResponseDto를 new로 새롭게 만들 때, 자동적으로 ResponseDto의 값도 "강의 목록 조회 성공", HttpStatus.OK.value() 이렇게 지정해준 값으로 넘어감
    public CourseListResponseDto(){
        super("강의 목록 조회 성공", HttpStatus.OK.value());
    }

    //List<CourseResponseDto> courseList = new ArrayList<>(); 이 안에 값을 넣어줄 코드
    //void: 반환 타입 필요 없으므로. addCourse: 이름
    public void addCourse(CourseResponseDto responseDto) {
        courseList.add(responseDto);    //받아온 responseDto를 넣음
    }
}

9. CourseDate.json

[
  {
    "title": "웹개발의 봄, 스프링",
    "instructor": "남병관 튜터",
    "cost": 500000
  },
  {
    "title": "웹개발 종합반",
    "instructor": "이범규 튜터",
    "cost": 450000
  },
  {
    "title": "액셀보다 쉬운 SQL",
    "instructor": "이범규 튜터",
    "cost": 500000
  },
  {
    "title": "알고보면 알기쉬운 알고리즘",
    "instructor": "박현준 튜터",
    "cost": 420000
  },
  {
    "title": "가장 쉽게 배우는 머신러닝",
    "instructor": "이태희 튜터",
    "cost": 390000
  },
  {
    "title": "게임개발 종합반",
    "instructor": "이동현 튜터",
    "cost": 430000
  },
  {
    "title": "Zep으로 메타버스 개발 맛보기",
    "instructor": "김상엽 튜터",
    "cost": 0
  },
  {
    "title": "제페토 메타버스 뉴월드 게임개발 종합반",
    "instructor": "최규빈 튜터",
    "cost": 330000
  }
]

테스트를 위한 JSON 타입 데이터

profile
개발자로 거듭나기!

0개의 댓글