//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);
}
}
//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("강의를 찾을 수 없다")
);
}
}
//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();
}
}
//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 넣으면 안됨
//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;
}
//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;
}
}
//강의 리스트 조회(전체 조회)
//개별 강의 조회
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();
}
}
//강의 리스트 조회(전체 조회)
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를 넣음
}
}
[
{
"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 타입 데이터