์ผ๋จ ์ฌ์ฉ์๋ถํฐ...
โ ADD ์ฌ์ฉ์๋ ์ผ๋จ add๋ง! ํ์ ํธ์ง/ํํด๋ ์ผ๋จ ๋์ค์.. ์กฐํ๋ ํ์์๋ค
๐ง User.java
@Data
@Builder
public class User {
private Integer id;
private String username;
private String password;
}
๐๏ธ UserRepository.java
@Repository
@RequiredArgsConstructor
public class UserRepository {
private final JdbcTemplate jdbcTemplate;
private final RowMapper<User> userRowMapper = (resultSet, rowNum) -> {
User user = User.builder()
.id(resultSet.getInt("id"))
.username(resultSet.getString("username"))
.password(resultSet.getString("password"))
.build();
return user;
};
public User findByUsername(String username) {
String sql = "SELECT * FROM users WHERE username = ?";
try {
return jdbcTemplate.queryForObject(sql, userRowMapper, username);
} catch (EmptyResultDataAccessException e) {
return null;
}
}
public int save(User user) {
String sql = "INSERT INTO users (username, password) VALUES (?, ?)";
return jdbcTemplate.update(sql, user.getUsername(), user.getPassword());
}
}
๐ซ ํ์๊ฐ์
๊ธฐ๋ฅ
๐ SignupDto.java
@Getter
@Setter
public class SignupDto {
@NotBlank(message = "์์ด๋๋ฅผ ์
๋ ฅํ์ธ์")
@Size(min=3, max=10, message = "์์ด๋๋ 3~10์์ฌ์ผ ํฉ๋๋ค")
private String username;
@NotBlank(message = "๋น๋ฐ๋ฒํธ๋ฅผ ์
๋ ฅํ์ธ์")
@Size(min=6, max=20, message = "๋น๋ฐ๋ฒํธ๋ 6~20์์ฌ์ผ ํฉ๋๋ค")
private String password;
}
๐ก SignupController.java
@Controller
@RequiredArgsConstructor
public class SignupController {
private final UserRepository userRepository;
@GetMapping("/signup")
public String showSingup(Model model) {
model.addAttribute("signupDto", new SignupDto());
return "signup";
}
@PostMapping("/signup")
public String doSignup(
@Valid @ModelAttribute("signupDto") SignupDto signupDTO,
BindingResult bindingResult,
Model model
) {
if (bindingResult.hasErrors()) {
return "signup";
}
// ์ค๋ณต ๊ฐ์
์ฌ๋ถ ์ฒดํฌ
if (userRepository.findByUsername((signupDTO.getUsername())) != null) {
model.addAttribute("error", "์ด๋ฏธ ์ฌ์ฉ์ค์ธ ์์ด๋์
๋๋ค");
return "signup";
}
User user = User.builder()
.username(signupDTO.getUsername())
.password(signupDTO.getPassword())
.build();
userRepository.save(user);
System.out.println("signupDto is null? " + (signupDTO == null));
return "redirect:/login?registered";
}
}
| ๊ตฌ๋ฌธ | ์ค๋ช |
|---|---|
| showSingup | ํด๋ผ์ด์ธํธ(์น์๋ฒ)๊ฐ get ์์ฒญ์ ํ์๋ = ์ฃผ์์ฐฝ์ /signup ์ฃผ์๋ก ๋ค์ด์์ ๋ ์ผ๋จ ํ์๊ฐ์ dto๋ฅผ ๋ชจ๋ธ์ ๋ด์์ ๋ณด๋ด๋ฉด ๋๋ค. |
| doSignup | ํด๋ผ์ด์ธํธ๊ฐ form[method=post]์ ํตํด model์ th:object="${signupDto}" ์ด๋ ๊ฒ dto๋ฅผ ๋ด์ ๋ณด๋ด์ค๊ฒ์ด๊ธฐ ๋๋ฌธ์, dto๋ฅผ ๋ฐ์์ฃผ๋ @Valid @ModelAttribute("signupDto") SignupDto signupDTO ์ถ๊ฐํ๋ค. ๊ทธ๋ฌ๋ฉด ์์์ ๋ฐ์ดํฐ๊ฐ dto์ ๋ฐ์์ง๋ฉด์ validation ์ฒดํฌ๋ฅผ ํด์ค๋ค. ์! |
| if (bindingResult.hasErrors()) | @Valid์ ๊ฒฐ๊ณผ ์๋ฌ ๋ฉ์์ง๋ bindingResult์ ๋ด๊ธฐ๊ธฐ ๋๋ฌธ์ ์ ์ธ์ ํด์ฃผ์ด์ผ ํ๋ค.. ๊ทธ๋ฆฌ๊ณ ์๋ฌ ๋ฉ์์ง๋ฅผ ํ๋ฉด์ ์ถ๋ ฅ์ ํด์ค์ผ ํ๊ธฐ๋๋ฌธ์ ์ด์จ๋ signup ํ๋ฉด์ ๋ค์ ๋์์ค๋ค. |
| ์ค๋ณต ๊ฐ์ ์ฌ๋ถ ์ฒดํฌ | ์ง๊ธ ๋จ๊ณ์์ Repository์ findByUsername ์ฟผ๋ฆฌ๊ฐ ์๊ธฐ๋๋ฌธ์ ์ผ๋จ ํจ์ค |
| User user = User.builder() ใ คใ คใ คใ คใ คใ คใ คใ คใ คใ คใ คใ คใ คใ คใ คใ คใ คใ คใ คใ คใ คใ คใ คใ คใ ค | ์ ์ ํ ๋ ํด๋ผ์ด์ธํธ์์ ๋์ง ์ํฐํฐ ์์ฒด๋ฅผ ๊ทธ๋๋ก ๋ฆฌํฌ์งํ ๋ฆฌ์ ์ ์ฅํ๋ฉด ๋์๊ธฐ ๋๋ฌธ์ ์ปจํธ๋กค๋ฌ์์ new๋ก ์์ฑํด์ค ํ์๊ฐ ์์๋๋ฐ ์ด์ save ํ๊ธฐ ์ํด์ dto๋ก ๋ฐ์ ๋ด์ฉ์ entity๋ก ๋ณํํด์ ์ ์ฅํด ์ฃผ์ด์ผ ํ๋ค.. |
๐ผ๏ธ Signup.html
<body>
<h1>๐ ํ์๊ฐ์
</h1>
<div th:if="${error}" class="error" th:text="${error}"></div>
<form th:action="@{/signup}" th:object="${signupDto}" method="post" class="form-container">
<p>
<label>
์์ด๋:
<input type="text" th:field="*{username}"/>
<div th:if="${#fields.hasErrors('username')}" th:errors="*{username}"></div>
</label>
</p>
<p>
<label>
ํจ์ค์๋:
<input type="password" th:field="*{password}"/>
<div th:if="${#fields.hasErrors('password')}" th:errors="*{password}"></div>
</label>
</p>
<button type="submit">๊ฐ์
ํ๊ธฐ</button>
<a th:href="@{/login}" class="cancel">๋ก๊ทธ์ธ</a>
</form>
</body>
<div th:if="${#fields.hasErrors('username')}" th:errors="*{username}"></div>๋ก๊ทธ์ธ ๊ธฐ๋ฅ ๊ตฌํ ์ ์ธ์ ์ ๊ดํด ์์๋ณด๊ธฐ
๐ก ์ธ์
stateless html : ์ํ๊ฐ ์๋ค. ์๋ฒ๊ฐ ๋ค์ด์ค๋ ์ฌ๋์ ์ํ๋ฅผ ๊ธฐ์ตํ์ง ์๋๋ค.
๋ง์ฝ ๊ธฐ์ต์ ํ๋ฉด์???
statefull -> ์๋ฒ: A์ ์ ๊ฐ ๋ค์ด์๊ตฐ, ํ์๊ฐ์
์ ํ๊ณ ํฌ๋๋ฅผ ์์ฑํ๊ตฐ,, ์๊ณ ์๋ค.
์ํ๊ฐ ์๋ค๋๊ฑด???
stateless -> ์๋ฒ: A์ธ์ง ์๋์ง ๋งค๋ฒ ๋ชจ๋ฅธ๋ค. ๋ฌด์์ ํ๊ฑด๊ฐ์ ๊ณ์ํด์ password๋ฅผ ๋ฐ์ A์์ ํ๋ณํด์ผ ํ๋ค.
๐ ๊ทผ๋ฐ ๊ณ์ํด์ ๋น๋ฒ์ ๋คํธ์ํฌ์ ํ์ฐ๋๊ฒ์??? ๋๋ฌด ๋ณด์์ ์ทจ์ฝํ๋ค.
์ด๊ฑฐ์ฌ ํด๊ฒฐํ๊ธฐ ์ํด
HttpSession httpSession,
httpSession.setAttribute("user", user);
๐ซ ๋ก๊ทธ์ธ ๊ธฐ๋ฅ
๐ LoginDto
@Getter
@Setter
public class SignupDto {
@NotBlank(message = "์์ด๋๋ฅผ ์
๋ ฅํ์ธ์")
@Size(min=3, max=10, message = "์์ด๋๋ 3~10์์ฌ์ผ ํฉ๋๋ค")
private String username;
@NotBlank(message = "๋น๋ฐ๋ฒํธ๋ฅผ ์
๋ ฅํ์ธ์")
@Size(min=6, max=20, message = "๋น๋ฐ๋ฒํธ๋ 6~20์์ฌ์ผ ํฉ๋๋ค")
private String password;
}
๐ก Controller
@Controller
@RequiredArgsConstructor
public class LoginController {
private final UserRepository userRepository;
@GetMapping({"/", "/login"})
public String showLogin(Model model) {
model.addAttribute("loginDto", new LoginDto());
return "login";
}
@PostMapping("/login")
public String doLogin(@Valid @ModelAttribute("loginDto") LoginDto loginDto, BindingResult bindingResult, HttpSession httpSession, Model model) {
if (bindingResult.hasErrors()) {
return "login";
}
try {
User user = userRepository.findByUsername(loginDto.getUsername());
if (!user.getPassword().equals(loginDto.getPassword())) {
model.addAttribute("error", "๋น๋ฐ๋ฒํธ๊ฐ ์ฌ๋ฐ๋ฅด์ง ์์ต๋๋ค");
return "login";
}
httpSession.setAttribute("user", user);
return "redirect:/todos";
} catch (Exception e) {
model.addAttribute("error", "์กด์ฌํ์ง ์๋ ์ฌ์ฉ์์
๋๋ค.");
return "login";
}
}
@GetMapping("/logout")
public String logout(HttpSession session) {
session.invalidate();
return "redirect:/login";
}
}
๐ผ๏ธ login.html
<body>
<h1>๐ ๋ก๊ทธ์ธ</h1>
<div th:if="${param.registered}" class="success">
๐ ํ์๊ฐ์
์ด ์๋ฃ๋์์ต๋๋ค! ๋ก๊ทธ์ธ ํด์ฃผ์ธ์.
</div>
<div th:if="${error}" class="error" th:text="${error}"></div>
<form th:action="@{/login}" th:object="${loginDto}" method="post" class="form-container">
<p>
<label>
์์ด๋:
<input type="text" th:field="*{username}" />
</label>
<div th:if="${#fields.hasErrors('username')}" th:errors="*{username}"></div>
</p>
<p>
<label>
ํจ์ค์๋:
<input type="password" th:field="*{password}" />
</label>
<div th:if="${#fields.hasErrors('password')}" th:errors="*{password}"></div>
</p>
<button type="submit">๋ก๊ทธ์ธ</button>
<a th:href="@{/signup}" class="cancel">ํ์๊ฐ์
</a>
</form>
</body>
Todo
๐ง Todo.java
@Data
@Builder
public class Todo {
private Integer id;
private String title;
private boolean completed;
private Integer userId;
}
๐๏ธ TodoRepository
@Repository
@RequiredArgsConstructor
public class TodoRepository {
private final JdbcTemplate jdbcTemplate;
// ๐ฏ ResultSet โ Todo ๊ฐ์ฒด๋ก ๋ณํํด์ฃผ๋ RowMapper ์ ์
private final RowMapper<Todo> todoRowMapper = (resultSet, rowNum) -> {
return Todo.builder()
.id(resultSet.getInt("id"))
.title(resultSet.getString("title"))
.completed(resultSet.getBoolean("completed"))
.userId(resultSet.getInt("user_id"))
.build();
};
// โ
๋ก๊ทธ์ธํ ์ฌ์ฉ์์ todo ๋ชฉ๋ก๋ง ์กฐํ (๋ณด์ ์ค์)
public List<Todo> findAllByUserId(int userId) {
String sql = "SELECT * FROM todo WHERE user_id = ? ORDER BY id";
return jdbcTemplate.query(sql, todoRowMapper, userId);
}
// โ
๋ก๊ทธ์ธํ ์ฌ์ฉ์์ ํน์ todo 1๊ฐ ์กฐํ
// โ ๏ธ userId ์กฐ๊ฑด์ ํจ๊ป ๊ฑธ์ด์ผ ๋ค๋ฅธ ์ฌ์ฉ์์ todo ์ ๊ทผ ์ฐจ๋จ ๊ฐ๋ฅ
public Todo findByIdAndUserId(int id, int userId) {
String sql = "SELECT * FROM todo WHERE id = ? AND user_id = ?";
return jdbcTemplate.queryForObject(sql, todoRowMapper, id, userId);
}
// โ
์ todo ๋ฑ๋ก (๋ก๊ทธ์ธํ ์ฌ์ฉ์์ userId๋ฅผ ํจ๊ป ์ ์ฅ)
public int save(Todo todo) {
String sql = "INSERT INTO todo (user_id, title, completed) VALUES (?, ?, ?)";
return jdbcTemplate.update(sql, todo.getUserId(), todo.getTitle(), todo.isCompleted());
}
// โ
๊ธฐ์กด todo ์์ (ํด๋น ์ฌ์ฉ์์ todo์ธ์ง ๊ฒ์ฆํ๊ธฐ ์ํด userId ์กฐ๊ฑด ํฌํจ)
public int update(Todo todo) {
String sql = "UPDATE todo SET title = ?, completed = ? WHERE id = ? AND user_id = ?";
return jdbcTemplate.update(sql, todo.getTitle(), todo.isCompleted(), todo.getId(), todo.getUserId());
}
// โ
todo ์ญ์ (์ฌ์ฉ์ ๋ณธ์ธ์ todo์ธ์ง ํ์ธ ์ํด userId ํฌํจ)
public int deleteByIdAndUserId(int id, int userId) {
String sql = "DELETE FROM todo WHERE id = ? AND user_id = ?";
return jdbcTemplate.update(sql, id, userId);
}
}
๐ก ToDoController
// TodoController๋ ์ฌ์ฉ์์ To-do ๋ฆฌ์คํธ๋ฅผ ๊ด๋ฆฌํ๋ ์ปจํธ๋กค๋ฌ์
๋๋ค.
@Controller
@RequestMapping("/todos") // /todos๋ก ์์ํ๋ URL์ ์ฒ๋ฆฌํฉ๋๋ค.
@RequiredArgsConstructor // final ํ๋๋ฅผ ์๋์ผ๋ก ์์ฑ์ ์ฃผ์
ํฉ๋๋ค.
public class TodoController {
// Todo ๋ฐ์ดํฐ๋ฅผ ์ฒ๋ฆฌํ Repository ์ฃผ์
private final TodoRepository todoRepository;
// ํ์ฌ ๋ก๊ทธ์ธํ ์ฌ์ฉ์๋ฅผ ์ธ์
์์ ๊ฐ์ ธ์ค๋ ์ ํธ๋ฆฌํฐ ๋ฉ์๋
private User getCurrentUser(HttpSession session) {
return (User) session.getAttribute("user");
}
// ์ ์ฒด To-do ๋ฆฌ์คํธ๋ฅผ ๋ณด์ฌ์ฃผ๋ ๋ฉ์๋
@GetMapping
public String list(HttpSession session, Model model) {
User user = getCurrentUser(session); // ํ์ฌ ์ฌ์ฉ์ ์กฐํ
System.out.println(getCurrentUser(session));
if(user == null) {
return "redirect:/login"; // ๋ก๊ทธ์ธ ์ํ์ผ๋ฉด ๋ก๊ทธ์ธ ํ์ด์ง๋ก ๋ฆฌ๋ค์ด๋ ํธ
}
// ํ์ฌ ์ฌ์ฉ์ ID๋ก ํ ์ผ ๋ชฉ๋ก ์กฐํ
List<Todo> list = todoRepository.findAllByUserId(user.getId());
// ๋ทฐ์ ํ ์ผ ๋ชฉ๋ก ์ ๋ฌ
model.addAttribute("todos", list);
return "todo-list"; // todo-list.html ๋ทฐ ๋ ๋๋ง
}
// ํ ์ผ ์ถ๊ฐ ํผ ์์ฒญ ์ฒ๋ฆฌ
@GetMapping("/add")
public String addForm(HttpSession httpSession, Model model) {
if (getCurrentUser(httpSession) == null) return "redirect:/login"; // ๋ก๊ทธ์ธ ํ์ธ
model.addAttribute("todoDto", new TodoDto()); // ๋น ํผ ๊ฐ์ฒด ์ ๋ฌ
return "todo-form"; // todo-form.html ๋ทฐ ๋ ๋๋ง
}
// ํ ์ผ ์ถ๊ฐ ์์ฒญ ์ฒ๋ฆฌ
@PostMapping("/add")
public String add(
@Valid @ModelAttribute TodoDto todoDto, // ์
๋ ฅ๊ฐ ์ ํจ์ฑ ๊ฒ์ฌ
BindingResult bindingResult, // ๊ฒ์ฌ ๊ฒฐ๊ณผ ์ ์ฅ
HttpSession httpSession
) {
if(bindingResult.hasErrors()) return "todo-form"; // ์๋ฌ๊ฐ ์์ผ๋ฉด ๋ค์ ํผ์ผ๋ก
User user = getCurrentUser(httpSession); // ๋ก๊ทธ์ธ ์ฌ์ฉ์ ์กฐํ
// Todo ๊ฐ์ฒด ์์ฑ ๋ฐ ์ ์ฅ
Todo todo = Todo.builder()
.userId(user.getId())
.title(todoDto.getTitle())
.completed(todoDto.isCompleted())
.build();
todoRepository.save(todo); // DB์ ์ ์ฅ
return "redirect:/todos"; // ๋ชฉ๋ก ํ์ด์ง๋ก ๋ฆฌ๋ค์ด๋ ํธ
}
// ํ ์ผ ์์ ํผ ์์ฒญ ์ฒ๋ฆฌ
@GetMapping("/edit/{id}")
public String editForm(
@PathVariable int id, // ์์ ํ To-do์ ID
Model model,
HttpSession httpSession
){
User user = getCurrentUser(httpSession);
if (user == null) return "redirect:/login";
// ํด๋น ID์ ์ฌ์ฉ์ ID๋ก ํ ์ผ ์กฐํ
Todo todo = todoRepository.findByIdAndUserId(id, user.getId());
// DTO๋ก ๋ณํํ์ฌ ํผ์ ์ ๋ฌ
TodoDto dto = new TodoDto();
dto.setId(todo.getId());
dto.setTitle(todo.getTitle());
dto.setCompleted(todo.isCompleted());
model.addAttribute("todoDto", dto);
return "todo-form"; // ๊ฐ์ ํผ์ ์ฌ์ฌ์ฉ
}
// ํ ์ผ ์์ ์์ฒญ ์ฒ๋ฆฌ
@PostMapping("/edit")
public String edit(
@Valid @ModelAttribute TodoDto todoDto,
BindingResult bindingResult,
HttpSession httpSession
) {
if(bindingResult.hasErrors()) return "todo-form"; // ์๋ฌ ์ ํผ์ผ๋ก
User user = getCurrentUser(httpSession);
// ์์ ๋ ๋ด์ฉ์ผ๋ก Todo ๊ฐ์ฒด ์์ฑ
Todo todo = Todo.builder()
.id(todoDto.getId())
.title(todoDto.getTitle())
.completed(todoDto.isCompleted())
.userId(user.getId())
.build();
todoRepository.update(todo); // ์
๋ฐ์ดํธ ์คํ
return "redirect:/todos";
}
// ํ ์ผ ์ญ์ ์์ฒญ ์ฒ๋ฆฌ
@PostMapping("/delete/{id}")
public String delete(
@PathVariable int id,
HttpSession httpSession
) {
User user = getCurrentUser(httpSession);
// ์ฌ์ฉ์ ID์ ํจ๊ป ์ญ์ (๋ณด์์ ํ์)
todoRepository.deleteByIdAndUserId(id, user.getId());
return "redirect:/todos";
}
}
@Valid @ModelAttribute์ BindingResult๋ ๋ฐ๋์ ์ฐ๋ฌ์ ์ฌ์ฉํด์ผ ํ๋ค.@ModelAttribute๋ก ๋ฐ์ธ๋ฉ๋ ๊ฐ์ฒด์ ๋ํด ์ ํจ์ฑ ๊ฒ์ฌ๋ฅผ ์ ์ฉํ ๋ @Valid๋ฅผ ๋ถ์BindingResult๋ ๊ฒ์ฌ ๊ฒฐ๊ณผ๋ฅผ ๋ด๋ ๊ฐ์ฒด์ด๋ฉฐ, @Valid ๋ฐ๋ก ๋ค์ ์ ์ธํด์ผ ์ค๋ฅ๋ฅผ ๊ฐ์งํ ์ ์์public String submit(@Valid @ModelAttribute FormDto formDto, BindingResult result)
๐ ๋ฑ๋ก๊ณผ ์์ ํผ์ ํ๋์ ํ
ํ๋ฆฟ(todo-form)์ผ๋ก ์ฌ์ฌ์ฉ
@GetMapping("/add") โ ๋น DTO ์ ๋ฌ@GetMapping("/edit/{id}") โ ๊ธฐ์กด ์ํฐํฐ๋ฅผ DTO๋ก ๋ณํํ์ฌ ์ ๋ฌ
๋ ๋ค ๊ฐ์ ํผ์ ์ฌ์ฉํ๋ฏ๋ก todo-form.html์ ์ฌํ์ฉํ ์ ์์
@GetMapping("/delete/{id}")์ฒ๋ผ GET ์์ฒญ์ผ๋ก ์ญ์ ํ์ง ์๋๋ก ์ฃผ์๋ ์ถ๊ฐํ ์ฌํญ : ๋น๋ฐ๋ฒํธ ์ ์ฅ ๊ธฐ๋ฅ ๋ฌ์๋ณด๊ธฐ