students) {
+ return students.stream()
+ .map(student -> StudentDto.from(student))
+ .collect(Collectors.toList());
+ }
+ }
+}
diff --git a/week03/minji/dbendpagination/src/main/java/com/codingbottle/dbendpagination/api/studentinlecture/controller/StudentInLectureController.java b/week03/minji/dbendpagination/src/main/java/com/codingbottle/dbendpagination/api/studentinlecture/controller/StudentInLectureController.java
new file mode 100644
index 0000000..ae93b5e
--- /dev/null
+++ b/week03/minji/dbendpagination/src/main/java/com/codingbottle/dbendpagination/api/studentinlecture/controller/StudentInLectureController.java
@@ -0,0 +1,179 @@
+package com.codingbottle.dbendpagination.api.studentinlecture.controller;
+
+import com.codingbottle.dbendpagination.api.common.RspTemplate;
+import com.codingbottle.dbendpagination.api.studentinlecture.dto.PenaltyReqDto;
+import com.codingbottle.dbendpagination.domain.studentinlecture.Penalty;
+import com.codingbottle.dbendpagination.domain.studentinlecture.StudentInLecture;
+import com.codingbottle.dbendpagination.domain.studentinlecture.StudentInLectureService;
+import lombok.RequiredArgsConstructor;
+import org.springframework.http.HttpStatus;
+import org.springframework.web.bind.annotation.*;
+
+/** // 백엔드 애플리케이션 개발을 위한 최소 역량 ?
+ * // 우리의 취업전선 경쟁자는 스스로 공부를 하는 사람들!!!
+ *
+ * // '스프링 부트' 라는 이름이 있는 책이나 강의 사서 배우면 좋아요.
+ *
+ * 1. 스프링 기초 이론 (의존성 주입, 빈 컨테이너(IOC Container), thread per request)
+ * 2. DB 기초 이론 (적어도 간단한 join문 작성 가능하고, 트랜잭션이 왜 존재하는 건지 알고 있음)
+ * 3. JPA 이론 (persistence context 의 1차 캐시, 스냅샷, DB flush 타이밍 등의 개념 'JPA 김영한씨 책' )
+ * 4. HTTP 기초 (HTTP Method들의 의미, 자주 사용되는 상태코드와 그것의 의미, 자주 사용되는 HTTP Header의 사용예시, SOP-CORS 이해)
+ * 5. 개발 프로세스에 대한 경험과 감각 (언어 숙련도, 가독성-재사용성 좋은 코드 짜기, 빈틈없는 코드를 짜고 테스트 잘 하기)
+ */
+@RequiredArgsConstructor
+@RestController
+public class StudentInLectureController {
+ private final StudentInLectureService studentInLectureService;
+
+ /**
+ * 1. 수강신청 API 만들기
+ * url - 계층구조.
+ * url - [POST] /student-in-lectures/lectures/1/students/1
+ *
+ * 그럼 StudentInLecture 객체 생성해서 저장 가능
+ * penaltyState는 NONE
+ */
+ // 수강신청
+ // studentId는 사실 일반적인 경우 넣을 필요가 없는데
+ // 아직 저희가 '인증' 을 배우지 않아서 그러함.
+ @PostMapping("/student-in-lectures/lectures/{lectureId}/students/{studentId}")
+ // handler method
+ public RspTemplate handleCreateStudentInLecture(
+ @PathVariable Long lectureId, @PathVariable Long studentId
+ ) {
+ // 수강신청 객체 StudentInLecture를 생성해서 repo 객체의 save() 호출에서 저장하는 것이 목적.
+ // lectureId, studentId 로 강의와 학생을 파악 가능.
+ // service 계층을 호출해서 객체 생성.
+ long savedStuInLecId = studentInLectureService.create(lectureId, studentId);
+
+ return new RspTemplate<>(HttpStatus.OK
+ , savedStuInLecId + "번 수강신청 완료"
+ );
+// return ResponseEntity
+// // "/student-in-lectures/{savedStuInLecId}" 로 [GET] 요청을 보내면 방금 만든 데이터 보내준다는 의미.
+// .created(URI.create("/student-in-lectures/" + savedStuInLecId))
+// .build();
+ }
+
+ /**
+ * 2. 벌점부여 API 만들기
+ * 원데이 클래스라고 가정함
+ * n주차 이런 거 없음
+ * n주차를 구현하려면 테이블을 하나 더 만들었을 것 같음
+ *
+ * url : /student-in-lecture/1 // 여기서 이미 어떤 강의 어떤 학생인지가 정해져있음.
+ * PATCH PUT
+ * body: 벌점(지각, 결석 ENUM)
+ */
+
+ //수정 전 벌점 api
+// @PatchMapping("/student-in-lectures/{studentInLectureId}")
+// public RspTemplate handleUpdateStudentInLecture(
+// @PathVariable Long studentInLectureId
+// , @RequestBody PenaltyReqDto reqDto
+// ) {
+// // 1. '벌점'을 의미하는 요청값( json 형식) 을 받아서 StudentInLecture 객체를 update한다.
+// Penalty penalty = reqDto.getPenalty();
+// long updatedStuInLecId = studentInLectureService.updatePenalty(studentInLectureId, penalty);
+//
+// return new RspTemplate<>(HttpStatus.OK
+// , updatedStuInLecId + "번 수강신청의 벌점이 수정되었습니다.");
+// }
+
+ //수정 후 벌점 api
+ @PatchMapping("/student-in-lectures/{studentInLectureId}")
+ public RspTemplate handleUpdateStudentInLecture(
+ @PathVariable Long studentInLectureId,
+ @RequestBody PenaltyReqDto reqDto
+ ) {
+ // 1. '벌점'을 의미하는 요청값(json 형식)을 받아서 StudentInLecture 객체를 update한다.
+ Penalty penalty = reqDto.getPenalty();
+
+ // 2. StudentInLecture 객체를 찾아온다.
+ StudentInLecture studentInLecture = studentInLectureService.getById(studentInLectureId);
+
+ // 3. 학생의 총 벌점을 가져온다.
+ int totalPenalty = studentInLecture.getStudent().getTotalPenalty();
+
+ // 4. 이전 벌점 정보를 가져온다.
+ Penalty previousPenalty = Penalty.NONE;
+ if (studentInLecture.getPenalty() != null) {
+ previousPenalty = Penalty.values()[studentInLecture.getPenalty()];
+ }
+
+ // 5. 학생의 벌점을 업데이트한다.
+ totalPenalty -= previousPenalty.getValue(); // 이전 벌점 감산
+ totalPenalty += penalty.getValue(); // 새로운 벌점 누적
+ studentInLecture.getStudent().setTotalPenalty(totalPenalty);
+
+ // 6. StudentInLecture 객체의 벌점을 변경한다.
+ studentInLecture.setPenalty(penalty);
+
+ // 7. StudentInLecture를 저장하고 업데이트된 정보를 반환한다.
+ studentInLectureService.updatePenalty(studentInLectureId, penalty);
+
+ return new RspTemplate<>(HttpStatus.OK,
+ studentInLectureId + "번 수강신청의 벌점이 수정되었습니다. 현재 총 벌점: " + totalPenalty + "점");
+ }
+
+
+ /**
+ * **과제**
+ *
+ * 3. 요구사항 추가. 벌점부여 API 수정.
+ *
+ * 학생 자체의 벌점을 기록해야 함.
+ * DB상의 특정 컬럼이 해당 학생의 총 벌점을 나타내야 한다는 것! (예시: Student 객체에 Integer totalPenalty 필드)
+ *
+ * @PatchMapping("/student-in-lectures/{studentInLectureId}")
+ * 로 요청이 들어올 때,
+ *
+ * student 객체의 벌점 점수 (totalPenalty)를 누적해야 한다.
+ *
+ * 같은 요청을 여러 번 보내면, 두 번째 요청부터는 벌점이 덮어쓰기 형식으로 진행.
+ *
+ * 하나의 studentInLecture 를 대상으로
+ * 결석으로 벌점을 수정하는 요청을 N번 보내면
+ * 벌점이 10 + 10 + 10 = 30이 되는 것이 아니라
+ *
+ * 10으로 고정되어있어야 함.
+ *
+ * 10 2 0
+ * 0
+ *
+ * 학생1로 강의1, 2에 모두 수강신청을 해서
+ * id 1, 2를 가진 studentInLecture 데이터가 생성되었다고 가정.
+ *
+ * @PatchMapping("/student-in-lectures/1")에
+ * '결석' 벌점부과 요청을 보내면
+ * 학생1의 totalPenalty는 10점이 되어야 한다. (0 + 10 = 10)
+ *
+ * 이후 @PatchMapping("/student-in-lectures/2")에
+ * '지각' 벌점부과 요청을 보내면
+ * 학생1의 totalPenalty는 12점이 되어야 한다. (10 + 2 = 12)
+ *
+ * 그런데 처음 벌점부과 요청은 관리자의 실수였다고 한다!
+ * 다시 @PatchMapping("/student-in-lectures/1") 로
+ * '없음' 벌점부과 요청을 보내면
+ * '결석' 처리가 '없음' 으로 변경되어서
+ * 학생1의 totalPenalty는 2점이 되어야 한다. (12 - 10 = 2)
+ */
+
+ /**
+ * 5.
+ * Fetch Join + 페이징
+ * 특정 강좌를 듣고 있는 학생의 목록 출력해야 함
+ * Lecture는 ByID()로 따로 조회하고
+ * StudentInLecture Fetch Student Where Lecture.id = lectureId
+ *
+ * Fetch Join 왜하냐?
+ * 데이터를 다룰 때는 보수적으로 가는 게 좋음
+ * 전체 Eager - 필요할 때 Lazy는 그런 방법이 없을 뿐더러 예측이 힘듬
+ * 전체 Lazy - 필요할 때 Eager(Fetch Join)이 관리가 쉬움
+ */
+
+
+
+
+
+}
diff --git a/week03/minji/dbendpagination/src/main/java/com/codingbottle/dbendpagination/api/studentinlecture/dto/PenaltyReqDto.java b/week03/minji/dbendpagination/src/main/java/com/codingbottle/dbendpagination/api/studentinlecture/dto/PenaltyReqDto.java
new file mode 100644
index 0000000..c41f83f
--- /dev/null
+++ b/week03/minji/dbendpagination/src/main/java/com/codingbottle/dbendpagination/api/studentinlecture/dto/PenaltyReqDto.java
@@ -0,0 +1,11 @@
+package com.codingbottle.dbendpagination.api.studentinlecture.dto;
+
+import com.codingbottle.dbendpagination.domain.studentinlecture.Penalty;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+
+@Getter
+@NoArgsConstructor
+public class PenaltyReqDto { // 역직렬화
+ Penalty penalty;
+}
diff --git a/week03/minji/dbendpagination/src/main/java/com/codingbottle/dbendpagination/domain/lecture/Lecture.java b/week03/minji/dbendpagination/src/main/java/com/codingbottle/dbendpagination/domain/lecture/Lecture.java
new file mode 100644
index 0000000..1dcf45c
--- /dev/null
+++ b/week03/minji/dbendpagination/src/main/java/com/codingbottle/dbendpagination/domain/lecture/Lecture.java
@@ -0,0 +1,46 @@
+package com.codingbottle.dbendpagination.domain.lecture;
+
+import lombok.AccessLevel;
+import lombok.Builder;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+
+import javax.persistence.*;
+
+@Getter
+@NoArgsConstructor(access = AccessLevel.PROTECTED)
+@Entity
+public class Lecture {
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ private Long id;
+
+ @Column(nullable = false)
+ private String name;
+
+ @Builder
+ private Lecture(String name) {
+ this.name = name;
+ }
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/week03/minji/dbendpagination/src/main/java/com/codingbottle/dbendpagination/domain/lecture/LectureRepository.java b/week03/minji/dbendpagination/src/main/java/com/codingbottle/dbendpagination/domain/lecture/LectureRepository.java
new file mode 100644
index 0000000..77a5a94
--- /dev/null
+++ b/week03/minji/dbendpagination/src/main/java/com/codingbottle/dbendpagination/domain/lecture/LectureRepository.java
@@ -0,0 +1,6 @@
+package com.codingbottle.dbendpagination.domain.lecture;
+
+import org.springframework.data.jpa.repository.JpaRepository;
+
+public interface LectureRepository extends JpaRepository {
+}
diff --git a/week03/minji/dbendpagination/src/main/java/com/codingbottle/dbendpagination/domain/lecture/LectureService.java b/week03/minji/dbendpagination/src/main/java/com/codingbottle/dbendpagination/domain/lecture/LectureService.java
new file mode 100644
index 0000000..4f9d19c
--- /dev/null
+++ b/week03/minji/dbendpagination/src/main/java/com/codingbottle/dbendpagination/domain/lecture/LectureService.java
@@ -0,0 +1,23 @@
+package com.codingbottle.dbendpagination.domain.lecture;
+
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.Optional;
+
+@RequiredArgsConstructor
+@Transactional(readOnly = true)
+@Service
+public class LectureService {
+ private final LectureRepository lectureRepository;
+
+ public Lecture getById(Long lectureId) {
+ Optional optionalLecture = lectureRepository.findById(lectureId);
+
+ if (optionalLecture.isEmpty()) {
+ throw new IllegalArgumentException("해당 강의가 존재하지 않습니다.");
+ }
+ return optionalLecture.get();
+ }
+}
diff --git a/week03/minji/dbendpagination/src/main/java/com/codingbottle/dbendpagination/domain/student/Student.java b/week03/minji/dbendpagination/src/main/java/com/codingbottle/dbendpagination/domain/student/Student.java
new file mode 100644
index 0000000..2b28b86
--- /dev/null
+++ b/week03/minji/dbendpagination/src/main/java/com/codingbottle/dbendpagination/domain/student/Student.java
@@ -0,0 +1,47 @@
+package com.codingbottle.dbendpagination.domain.student;
+
+import lombok.*;
+
+import javax.persistence.*;
+
+@Getter
+@Setter
+@NoArgsConstructor(access = AccessLevel.PROTECTED)
+@Entity
+public class Student {
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ private Long id;
+
+ @Column(nullable = false)
+ private String name;
+
+ //왜 이 컬럼에는 not null이 안될까요? 이 어노테이션을 넣으면 error발생..
+ //@Column(nullable = false)
+ private Integer totalPenalty;
+
+ @Builder
+ private Student(String name) {
+ this.name = name;
+ }
+
+
+
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/week03/minji/dbendpagination/src/main/java/com/codingbottle/dbendpagination/domain/student/StudentRepository.java b/week03/minji/dbendpagination/src/main/java/com/codingbottle/dbendpagination/domain/student/StudentRepository.java
new file mode 100644
index 0000000..6f764e9
--- /dev/null
+++ b/week03/minji/dbendpagination/src/main/java/com/codingbottle/dbendpagination/domain/student/StudentRepository.java
@@ -0,0 +1,6 @@
+package com.codingbottle.dbendpagination.domain.student;
+
+import org.springframework.data.jpa.repository.JpaRepository;
+
+public interface StudentRepository extends JpaRepository {
+}
\ No newline at end of file
diff --git a/week03/minji/dbendpagination/src/main/java/com/codingbottle/dbendpagination/domain/student/StudentService.java b/week03/minji/dbendpagination/src/main/java/com/codingbottle/dbendpagination/domain/student/StudentService.java
new file mode 100644
index 0000000..b203e58
--- /dev/null
+++ b/week03/minji/dbendpagination/src/main/java/com/codingbottle/dbendpagination/domain/student/StudentService.java
@@ -0,0 +1,32 @@
+package com.codingbottle.dbendpagination.domain.student;
+
+import lombok.RequiredArgsConstructor;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.domain.Slice;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+@RequiredArgsConstructor
+@Transactional(readOnly = true)
+@Service
+public class StudentService {
+ private final StudentRepository studentRepository;
+
+ public Student getById(Long studentId) {
+ return studentRepository.findById(studentId)
+ .orElseThrow(() -> new IllegalArgumentException("해당 학생이 존재하지 않습니다."));
+ }
+
+ public Page findAll(Pageable pageable) {
+ // List
+ // Page
+ return studentRepository.findAll(pageable);
+ }
+
+ public Slice findAllSlice(Pageable pageable) {
+ // List
+ // Page
+ return studentRepository.findAll(pageable);
+ }
+}
diff --git a/week03/minji/dbendpagination/src/main/java/com/codingbottle/dbendpagination/domain/studentinlecture/Penalty.java b/week03/minji/dbendpagination/src/main/java/com/codingbottle/dbendpagination/domain/studentinlecture/Penalty.java
new file mode 100644
index 0000000..66294a2
--- /dev/null
+++ b/week03/minji/dbendpagination/src/main/java/com/codingbottle/dbendpagination/domain/studentinlecture/Penalty.java
@@ -0,0 +1,16 @@
+package com.codingbottle.dbendpagination.domain.studentinlecture;
+
+
+import lombok.Getter;
+
+@Getter
+public enum Penalty {
+ // 없음, 지각, 결석
+ NONE(0), LATE(2), ABSENT(10);
+
+ private final int value;
+
+ Penalty(int value) {
+ this.value = value;
+ }
+}
diff --git a/week03/minji/dbendpagination/src/main/java/com/codingbottle/dbendpagination/domain/studentinlecture/StudentInLecture.java b/week03/minji/dbendpagination/src/main/java/com/codingbottle/dbendpagination/domain/studentinlecture/StudentInLecture.java
new file mode 100644
index 0000000..ff057f3
--- /dev/null
+++ b/week03/minji/dbendpagination/src/main/java/com/codingbottle/dbendpagination/domain/studentinlecture/StudentInLecture.java
@@ -0,0 +1,60 @@
+package com.codingbottle.dbendpagination.domain.studentinlecture;
+
+import com.codingbottle.dbendpagination.domain.lecture.Lecture;
+import com.codingbottle.dbendpagination.domain.student.Student;
+import lombok.AccessLevel;
+import lombok.Builder;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+
+import javax.persistence.*;
+
+@Getter
+@NoArgsConstructor(access = AccessLevel.PROTECTED)
+@Entity
+public class StudentInLecture {
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ private Long id;
+
+ // 어떤 학생이 어떤 강의를 듣는지에 대한 정보.
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "student_id", nullable = false)
+ private Student student;
+
+ // 연관 객체가 필요할 때만 EAGER로 가져오게 하는 방법
+ // FETCH JOIN
+
+ // EAGER로 깔아둔 다음에, 필요할 때만 LAZY
+
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "lecture_id", nullable = false)
+ private Lecture lecture;
+
+ private Integer penalty;
+
+ public void setPenalty(Penalty penalty) {
+ this.penalty = penalty.getValue();
+ }
+
+ @Builder
+ private StudentInLecture(Student student, Lecture lecture) {
+ this.student = student;
+ this.lecture = lecture;
+ this.penalty = Penalty.NONE.getValue();
+ }
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/week03/minji/dbendpagination/src/main/java/com/codingbottle/dbendpagination/domain/studentinlecture/StudentInLectureRepository.java b/week03/minji/dbendpagination/src/main/java/com/codingbottle/dbendpagination/domain/studentinlecture/StudentInLectureRepository.java
new file mode 100644
index 0000000..6900173
--- /dev/null
+++ b/week03/minji/dbendpagination/src/main/java/com/codingbottle/dbendpagination/domain/studentinlecture/StudentInLectureRepository.java
@@ -0,0 +1,6 @@
+package com.codingbottle.dbendpagination.domain.studentinlecture;
+
+import org.springframework.data.jpa.repository.JpaRepository;
+
+public interface StudentInLectureRepository extends JpaRepository {
+}
\ No newline at end of file
diff --git a/week03/minji/dbendpagination/src/main/java/com/codingbottle/dbendpagination/domain/studentinlecture/StudentInLectureService.java b/week03/minji/dbendpagination/src/main/java/com/codingbottle/dbendpagination/domain/studentinlecture/StudentInLectureService.java
new file mode 100644
index 0000000..d820440
--- /dev/null
+++ b/week03/minji/dbendpagination/src/main/java/com/codingbottle/dbendpagination/domain/studentinlecture/StudentInLectureService.java
@@ -0,0 +1,75 @@
+package com.codingbottle.dbendpagination.domain.studentinlecture;
+
+import com.codingbottle.dbendpagination.domain.lecture.Lecture;
+import com.codingbottle.dbendpagination.domain.lecture.LectureService;
+import com.codingbottle.dbendpagination.domain.student.Student;
+import com.codingbottle.dbendpagination.domain.student.StudentService;
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.Optional;
+
+@RequiredArgsConstructor
+@Transactional(readOnly = true)
+@Service
+public class StudentInLectureService {
+ private final StudentInLectureRepository studentInLectureRepository;
+ private final StudentService studentService;
+ private final LectureService lectureService;
+
+ @Transactional
+ public long create(Long lectureId, Long studentId) {
+
+ // 1. id로 연관 객체(student, lecture)를 찾는다.
+ // Persistence Context (1차 저장소) 에 등록.
+ Lecture lecture = lectureService.getById(lectureId);
+ Student student = studentService.getById(studentId);
+
+ // 2. 연관 객체를 찾으면 StudentInLecture 객체를 생성하고 저장한다.
+ StudentInLecture studentInLecture = StudentInLecture.builder()
+ .student(student)
+ .lecture(lecture)
+ .build();
+
+ // 1차 저장소에 등록
+ StudentInLecture savedStuInLec = studentInLectureRepository.save(studentInLecture);
+ return savedStuInLec.getId();
+ // 메서드를 종료한 직후, 1차 저장소의 변경사항을 DB에 flush 처리하고,
+ // 트랜잭션을 commit;
+ }
+
+ @Transactional
+ public long updatePenalty(Long studentInLectureId, Penalty penalty) {
+ // 2. studentInLectureId 라는 경로 변수로 StudentInLecture 객체를 찾아온다.
+ StudentInLecture studentInLecture = getById(studentInLectureId);
+
+ // 3. 이전 벌점 정보를 가져온다.
+ Penalty previousPenalty = Penalty.NONE; // 기본값으로 설정
+ if (studentInLecture.getPenalty() != null) {
+ // studentInLecture.getPenalty()의 값이 null이 아닐 때만 변환을 시도하도록 변경
+ previousPenalty = Penalty.values()[studentInLecture.getPenalty()];
+ }
+
+ // 4. studentInLecture 객체의 벌점을 변경한다.
+ studentInLecture.setPenalty(penalty);
+
+ // 5. 학생의 총 벌점을 업데이트한다.
+ int updatedTotalPenalty = studentInLecture.getStudent().getTotalPenalty();
+ updatedTotalPenalty -= previousPenalty.getValue(); // 이전 벌점 감산
+ updatedTotalPenalty += penalty.getValue(); // 새로운 벌점 누적
+ studentInLecture.getStudent().setTotalPenalty(updatedTotalPenalty);
+
+ return studentInLecture.getId();
+ // 1차 저장소의 정보가 DB로 flush 되고, 트랜잭션이 commit 된다.
+ }
+
+ public StudentInLecture getById(Long studentInLectureId) {
+ Optional optionalStudentInLecture = studentInLectureRepository.findById(studentInLectureId);
+ if (optionalStudentInLecture.isEmpty()) {
+ throw new IllegalArgumentException("해당 수강신청이 존재하지 않습니다.");
+ }
+
+ return optionalStudentInLecture.get();
+ }
+}
diff --git a/week03/minji/dbendpagination/src/main/java/com/codingbottle/dbendpagination/global/util/InitDB.java b/week03/minji/dbendpagination/src/main/java/com/codingbottle/dbendpagination/global/util/InitDB.java
new file mode 100644
index 0000000..40ac68a
--- /dev/null
+++ b/week03/minji/dbendpagination/src/main/java/com/codingbottle/dbendpagination/global/util/InitDB.java
@@ -0,0 +1,48 @@
+package com.codingbottle.dbendpagination.global.util;
+
+
+import com.codingbottle.dbendpagination.domain.lecture.Lecture;
+import com.codingbottle.dbendpagination.domain.lecture.LectureRepository;
+import com.codingbottle.dbendpagination.domain.student.Student;
+import com.codingbottle.dbendpagination.domain.student.StudentRepository;
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Component;
+import org.springframework.transaction.annotation.Transactional;
+
+import javax.annotation.PostConstruct;
+
+@RequiredArgsConstructor
+@Component
+public class InitDB {
+ private final StudentRepository studentRepository;
+ private final LectureRepository lectureRepository;
+ // 애플리케이션 실행 시점에 빈 컨테이너에 단 하나의 객체(스프링 빈)을 등록해요
+
+ @Transactional
+ @PostConstruct
+ public void init() {
+ Lecture java = Lecture.builder()
+ .name("자바")
+ .build();
+ Lecture cpp = Lecture.builder()
+ .name("C++")
+ .build();
+ lectureRepository.save(java); lectureRepository.save(cpp);
+
+ Student kim = Student.builder()
+ .name("김코딩")
+ .build();
+ Student jung = Student.builder()
+ .name("정코딩")
+ .build();
+
+ int studentCount = 100;
+ for (int i = 0; i < studentCount; i+=1) {
+ Student student = Student.builder()
+ .name(i + "학생")
+ .build();
+ studentRepository.save(student);
+ }
+ studentRepository.save(kim); studentRepository.save(jung);
+ }
+}
diff --git a/week03/minji/dbendpagination/src/main/java/com/codingbottle/dbendpagination/global/util/PageableUtil.java b/week03/minji/dbendpagination/src/main/java/com/codingbottle/dbendpagination/global/util/PageableUtil.java
new file mode 100644
index 0000000..b2b4d00
--- /dev/null
+++ b/week03/minji/dbendpagination/src/main/java/com/codingbottle/dbendpagination/global/util/PageableUtil.java
@@ -0,0 +1,30 @@
+package com.codingbottle.dbendpagination.global.util;
+
+import lombok.AccessLevel;
+import lombok.NoArgsConstructor;
+import org.springframework.data.domain.PageRequest;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.domain.Sort;
+
+@NoArgsConstructor(access = AccessLevel.PRIVATE)
+public class PageableUtil {
+
+ /** 0-base인 페이지를 클라이언트단에서 1-based인 것처럼 사용할 수 있게 한다.
+ * @param oneBasedPage
+ * @param size
+ * @return 0-based pageable Instance
+ */
+ public static Pageable of(int oneBasedPage, int size) {
+ if (oneBasedPage < 1)
+ throw new IllegalArgumentException("page는 1 이상이어야 합니다.");
+
+ return PageRequest.of(oneBasedPage - 1 , size);
+ }
+
+ public static Pageable of(int oneBasedPage, int size, Sort sort) {
+ if (oneBasedPage < 1)
+ throw new IllegalArgumentException("page는 1 이상이어야 합니다.");
+
+ return PageRequest.of(oneBasedPage - 1 , size, sort);
+ }
+}
diff --git a/week03/minji/dbendpagination/src/main/resources/application.properties b/week03/minji/dbendpagination/src/main/resources/application.properties
new file mode 100644
index 0000000..3454495
--- /dev/null
+++ b/week03/minji/dbendpagination/src/main/resources/application.properties
@@ -0,0 +1,11 @@
+spring.datasource.url=jdbc:mysql://localhost:3306/cbweek2
+spring.datasource.username=root
+spring.datasource.password=1234
+spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
+spring.jpa.hibernate.ddl-auto=create
+spring.jpa.database=mysql
+spring.jpa.properties.hibernate.format_sql=true
+spring.jpa.properties.hibernate.default_batch_fetch_size=1000
+spring.jpa.show-sql=true
+spring.jpa.open-in-view=false
+logging.level.org.hibernate.type=TRACE
diff --git a/week03/minji/dbendpagination/src/main/resources/db.md b/week03/minji/dbendpagination/src/main/resources/db.md
new file mode 100644
index 0000000..03897a7
--- /dev/null
+++ b/week03/minji/dbendpagination/src/main/resources/db.md
@@ -0,0 +1,3 @@
+- CREATE DATABASE cbweek2
+ CHARACTER SET utf8mb4
+ COLLATE utf8mb4_unicode_ci; (case insensitive)
\ No newline at end of file
diff --git a/week03/minji/dbendpagination/src/test/java/com/codingbottle/dbendpagination/DbendpaginationApplicationTests.java b/week03/minji/dbendpagination/src/test/java/com/codingbottle/dbendpagination/DbendpaginationApplicationTests.java
new file mode 100644
index 0000000..b280ff7
--- /dev/null
+++ b/week03/minji/dbendpagination/src/test/java/com/codingbottle/dbendpagination/DbendpaginationApplicationTests.java
@@ -0,0 +1,13 @@
+package com.codingbottle.dbendpagination;
+
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.test.context.SpringBootTest;
+
+@SpringBootTest
+class DbendpaginationApplicationTests {
+
+ @Test
+ void contextLoads() {
+ }
+
+}