public class StudentDto {
private Long id;
private String name;
private String email;
public StudentDto() {
}
public StudentDto(Long id, String name, String email) {
this.id = id;
this.name = name;
this.email = email;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
@Override
public String toString() {
return "StudentDto{" +
"id=" + id +
", name='" + name + '\'' +
", email='" + email + '\'' +
'}';
}
}
import com.example.crud.model.StudentDto;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
@Service
public class StudentService {
// 복수의 StudentDto 를 담는 멤버변수 (필드)
private final List<StudentDto> studentList = new ArrayList<>();
private Long nextId = 1L;
public StudentService() {
createStudent("alex", "alex@gmail.com");
createStudent("brad", "brad@gmail.com");
createStudent("chad", "chad@gmail.com");
}
// 새로운 StudentDto 를 생성하는 메소드
public StudentDto createStudent(String name, String email) {
StudentDto newStudent = new StudentDto(
nextId, name, email
);
nextId++;
studentList.add(newStudent);
return newStudent;
}
public List<StudentDto> readStudentAll() {
return studentList;
}
// Service에서 단일 StudentDto를 주는 메소드를 만들겁니다
// 이때 이 메소드가 받을 인자는 무엇일까요?
public StudentDto readStudent(Long id) {
for (StudentDto studentDto: studentList) {
if (studentDto.getId().equals(id))
return studentDto;
}
return null;
// return studentList
// .stream()
// .filter(studentDto -> studentDto.getId().equals(id))
// .findFirst()
// .orElse(null);
}
// 어떤 학생 데이터를 갱신할 것인지
// 그 학생의 갱신될 데이터
public StudentDto updateStudent(Long id, String name, String email){
// 하나의 StudentDto를 찾아서
int target = -1;
// studentList의 크기만큼 반복
for (int i = 0; i < studentList.size(); i++) {
// id가 동일한 studentDto 찾았으면
if (studentList.get(i).getId().equals(id)) {
// 인덱스 기록
target = i;
// 반복 종료
break;
}
}
// 대상을 찾았다면
if (target != -1) {
// name과 email을 바꿔주자
studentList.get(target).setName(name);
studentList.get(target).setName(email);
return studentList.get(target);
}
// 대상을 못 찾았다면
else return null;
}
public boolean deleteStudent(Long id) {
int target = -1;
// 학생 리스트를 살펴보며
for (int i = 0; i < studentList.size(); i++) {
// 대상을 선정한다.
if (studentList.get(i).getId().equals(id)) {
target = i;
break;
}
}
// 검색 성공시
if (target != -1) {
studentList.remove(target);
return true;
}
else return false;
}
}
import com.example.crud.model.StudentDto;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
@Controller
public class StudentController {
// StudentService 를 Controller 에서 사용
private final StudentService studentService;
public StudentController(
StudentService studentService) {
this.studentService = studentService;
}
@GetMapping("/create-view")
public String createView() {
return "create";
}
@PostMapping("/create")
public String create(
@RequestParam("name")
String name,
@RequestParam("email")
String email
) {
System.out.println(name);
System.out.println(email);
StudentDto studentDto
= studentService.createStudent(name, email);
System.out.println(studentDto.toString());
// return "redirect:/create-view";
return "redirect:/home";
}
@GetMapping("/home")
public String home(Model model) {
model.addAttribute(
"studentList",
studentService.readStudentAll()
);
return "home";
}
@GetMapping("/{id}")
public String read(
@PathVariable("id") Long id,
Model model
) {
// studentService.readStudent(id);
System.out.println(id);
model.addAttribute(
"student",
studentService.readStudent(id)
);
return "read";
}
@GetMapping("/{id}/update-view")
public String updateView(
@PathVariable("id") Long id,
Model model
){
model.addAttribute(
"student",
studentService.readStudent(id)
);
return "update";
}
@PostMapping("/{id}/update")
public String update(
@PathVariable("id") Long id, String name, String email
) {
studentService.updateStudent(id, name, email);
return String.format("redirect:/%s", id);
}
@GetMapping("/{id}/delete-view")
public String deleteView(
@PathVariable("id")
Long id,
Model model
){
model.addAttribute(
"student",
studentService.readStudent(id)
);
return "delete";
}
@PostMapping("/{id}/delete")
public String delete(
@PathVariable("id")
Long id
) {
studentService.deleteStudent(id);
// update 때는 데이터가 남아있지만
// delete 는 돌아갈 상세보기가 없다
// 그래서 홈으로 돌아간다.
return "redirect:/home";
}
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>[[${student.id}]]. [[${student.name}]]</h1>
<p>이메일: [[${student.email}]]</p>
<a th:href="@{/{id}/update-view (id=${student.id})}">Update</a>
<a th:href="@{/{id}/delete-view (id=${student.id})}">Delete</a>
<a href="/home">Back</a>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Students Home</title>
</head>
<body>
<h1>Student List</h1>
<div th:if="${studentList.isEmpty()}">
<p>No students here...</p>
</div>
<div th:unless="${studentList.isEmpty()}" th:each="student: ${studentList}">
<p>번호: [[${student.id}]]</p>
<p>이름: <a th:href="@{/{id} (id=${student.id})}">[[${student.name}]]</a></p>
<p>이메일: [[${student.email}]]</p>
<hr>
</div>
<a href="/create-view">Create</a>
</body>
</html>
데이터 갱신을 위해서 대상 데이터를 가져오고 갱신할 새로운 데이터로 업데이트 해야한다.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Update</title>
</head>
<body>
<h1>Update Student</h1>
<form th:action="@{/{id}/update (id=${student.id})}" method="post">
<!-- 사용자가 제공할 데이터에 알맞은 Input과 label을 만든다. -->
<label for="name-input">Name: <input id="name-input" name="name" th:value="${student.name}"></label><br>
<label for="email-input">Email: <input id="email-input" name="email" th:value="${student.email}"></label><br>
<input type="submit">
</form>
<!-- <a href="/create-view">Create</a>-->
<a th:href="@{/{id} (id=${student.id})}">Back</a>
</body>
</html>
Create, Read 코드와 유사하다.
상세보기 페이지에서 삭제 여부를 확인하고 삭제 후 home으로 이동하기
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Delete</title>
</head>
<body>
<h1>Delete [[${student.name}]]?</h1>
<form th:action="@{/{id}/delete (id=${student.id})}" method="post">
<!-- create / update 와는 다르게 -->
<!-- delete 의 경우 추가적인 데이터를 필요로 하지 않는다. -->
<!-- 그래서 추가적인 input 요소 없이 input type="submit"만 만들면 된다. -->
<input type="submit" value="Delete">
</form>
<a th:href="@{/{id} (id=${student.id})}">Back</a>
</body>
</html>
PathVariable
은 url
에서 특정 데이터를 지정하는 등 한 가지 값
을 받을 수 있다.id
가 12
번인 학생의 마이페이지
를 가고 싶다.{ 학생의 아이디(12) }
RequestParam
의 경우 해당 위치에서 사용할 데이터를 담아 보내는 용도이다.id
12
번 마이페이지
에서 오늘 날짜에 출석 체크
를 하고 싶다.date=20230609&isOk=true
출처 : 멋사 백엔드 5기 11팀 11번가
Q. StudentService.class
에서 target = -1
인 이유?
https://www.acmicpc.net/problem/11501
드디어 간단한 모든 기능을 구현해보았다. 하지만 코드만 따라쳤을뿐(영타 실력 상승) 로직을 이해한건 10%도 채 안된것 같다. 이 어려운 것을 원기님께선 이미 마스터를 하셨다는데 정말 놀라운 사람이다. 오늘 인사이트 타임의 주역을 맡아주셨지만 왕초보 돌머리인 나로서는 MVC와 알고리즘을 온전히 이해할 수 없었다.
원기님의 팁으로는 직접 코드를 작성해보는게 실력이 늘 수 있는 가장 효율적인 방법이라고 추천해주셨다. 아마 이 기능을 혼자 구현하려면 지금보다 다섯배는 더 오래 걸릴것 같은데.. 중요한 건 분리 했던 Service와 Controller의 기능을 헷갈리지 않게 구분할 수 있도록 복습을 많이 해야 할 것 같다.