
์ด๋ก์คํฐ๋๋ฅผ ์งํํ๋ฉด์ Spring MVC์ ๋ํด์ ํ์ตํด๋ณด์๋ค.
Spring์ ์ฒ์ ์ฌ์ฉํ๋ค๋ณด๋ ์ฌ๋ฌ ๋ฌธ์ ๋ฅผ ๊ฒช์๋ค.
ํ์ต ํ
์คํธ๋ฅผ ๋ณด๊ณ ์ ๊ทธ๋ฐ์ง ์ดํด๋ชปํ๊ณ ๋์ด๊ฐ ๋ถ๋ถ์ด ๋ ๋ง์ ๊ฒ ๊ฐ๋ค. (์ง๊ธ๋...)
๋ฏธ์
์ ์งํํ๋ฉด์ ์๋ฌธ์ ๊ฐ์ก๋ ๋ถ๋ถ๊ณผ ๋ฐฐ์ด ๋ถ๋ถ์์ฃผ๋ก ์ ๋ฆฌ ํ๋ค.
https://github.com/day024/spring-roomescape-playground/tree/day024
๋ฏธ์
์ ๋ค์ด๊ฐ๊ธฐ ์์ ์คํ๋ง์ ์ฒ์ ์จ๋ณด๊ธฐ ๋๋ฌธ์ ๊ธฐ๋ณธ ์ธํ
๋ถํฐ ์งํ ํด๋ณด์๋ค.
์ด๋ค ๋ฌธ์ ์ธ์ง ์คํ๋ง์ด ์ธ์์ด ์๋๋ ์ค๋ฅ๊ฐ ๊ณ์ ๋ฐ์ํ์๋ค.
https://velog.io/@coastby/Spring ์ด๊ธ์ ํตํด์ ๊ฒจ์ฐ ํด๊ฒฐํ์๋ค.
์ฒ์์๋ Gradle๋ฒ์ ์ด ์๋ง๋๋ค๋ ์ค๋ฅ๊ฐ ๊ณ์๋ฐ์ํด์ Build,Execution,Deployment>gradle JVM ์ค์ ์ ๊ณ์ ๋ณ๊ฒฝํ๋ฉด์ ๋ฒ์ ์ ๋ฐ๊ฟ๋ณด์๋ค.

๊ทธ๋ฐ๋ฐ ์ด์ Cannot resolve symbol 'springframework ๋ผ๋ ๋ฌธ๊ตฌ๊ฐ ๊ณ์ ๋ด๊ณ ๋ธ๋ก๊ทธ๋ฅผ ํตํด์ ํด๊ฒฐํ๋ค.
์ด์งธ์์ธ์ง ๋ค๋ฅธ ๋ฐฉ๋ฒ์ผ๋ก๋ ์ฌ์ ํ ๋๊ฐ์ ์ค๋ฅ๋ฅผ ๋ฐ๋ณตํ๋ค. ๋์ผํ๊ฒ Gradle์ refresh ํด์ฃผ๋ ๋ฐฉ๋ฒ์ด ์ฑ๊ณตํ๋ค.
์๊ตฌ์ฌํญ:
ํ ํ๋ฉด์กฐํ / ์์กด์ฑ ์ถ๊ฐ
์น๊ด๋ จ ์์กด์ฑ์ ์ถ๊ฐํ๋ค.
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
@Controller์ @GetMapping์ ํตํด์ ์งํํ๋ค.
package roomescape.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class HomeController {
@GetMapping("/")
public String home() {
return "home";
}
}
์ด๋ ธํ ์ด์ ์ ํตํด์ ์คํ๋ง์์ ๋ค์ํ ๊ธฐ๋ฅ์ ์ํํ๋ค.
1. @Controller
2. @RequestMapping
@GetMapping, @GetMapping, @PostMapping, @PutMapping, @DeleteMapping, @PatchMapping 3. @GetMapping
@PathVariable ์ด๋
ธํ
์ด์
์ ํตํด์ ๊ฒฝ๋ก๋ฅผ ์ง์ ํ๊ธฐ๋ ํ๋ค. , @PathVariable ๋ฅผ ๋ค์ค์ฌ์ฉ๋ ๊ฐ๋ฅํ๋ค. ์๊ตฌ์ฌํญ:
์์ฝ ์กฐํ API๊ตฌํ
์์ฝ ํ์ด์ง ์์ฒญ๊ณผ ์์ฝ ๋ชฉ๋ก ์กฐํ ์์ฒญ์ ์ฒ๋ฆฌํ๋ ๋ฉ์๋๋ฅผ ๊ตฌํํ์ธ์.
reservation๋ฅผ ์กฐํํ๋ ๊ฒ์ 1๋จ๊ณ homeController์ ๊ฐ๊ฒ ์งํํ์๋ค.ResponseEntity.status(HttpStatus.OK).body(reservationList);
ResponseEntity๋ฅผ ์์ฑ ํ๊ณ ์๋ต์ ์ํ๋ฅผ HttpStatus.OK, ์ํ์ฝ๋ 200์ผ๋ก ์ค์ ํ๋ค. ์์ฒญ์ด ์ฑ๊ณต์ ์ผ๋ก ์คํ ๋์๋ค๋ ์๋ฏธ. body(reservationList) ์์ฝ ๋ด์ฉ์ด ๋ด๊ธด ๋ฆฌ์คํธ๋ฅผ ์๋ต ๋ณธ๋ฌธ์ผ๋ก ์ ๋ฌ@Controller
public class ReservationController {
private List<Reservation> reservations = new ArrayList<>();
//์์ฝ ๋ทฐ ๋ ๋๋ง
@GetMapping("/reservation")
public String reservation() {
return "reservation";
}
//์์ฝ ์ ๋ณด๋ฅผ ์กฐํ
@GetMapping("/reservations")
public ResponseEntity<List<Reservation>> getReservations() {
List<Reservation> reservationList = new ArrayList<>();
reservationList.add(new Reservation(1, "๋ธ๋ผ์ด", "2023-01-01", "10:00"));
reservationList.add(new Reservation(2, "๋ธ๋ผ์ด", "2023-01-02", "11:00"));
return ResponseEntity.status(HttpStatus.OK).body(reservationList);
}
Reservation class๋ฅผ ์์ฑํ๋ค. ์์ฝ ์ ๋ณด๋ฅผ ์ ์ฅํ๊ธฐ ์ํ id, name, date, time์ ๊ฐ์ง๊ณ ์๋ class์ด๋ค.
package roomescape.domain;
import java.time.LocalDateTime;
public class Reservation {
private int id;
private String name;
private String date;
private String time;
public Reservation(int id, String name, String date, String time) {
this.id = id;
this.name = name;
this.date = date;
this.time = time;
}
public int getId() {
return id;
}
public String getDate() {
return date;
}
public String getName() {
return name;
}
public String getTime() {
return time;
}
}
์๊ตฌ์ฌํญ :
API ๋ช
์ธ๋ฅผ ๋ฐ๋ผ ์์ฝ ์ถ๊ฐ API ์ ์ญ์ API๋ฅผ ๊ตฌํ
id๋ฅผ ๋ง๋ค๊ณ , ์ ์์ฝ ๊ฐ์ฒด๋ฅผ ์์ฑํ๋ค.
์์ฝ์ ์ ์ฅํ๊ณ ResponseEntity๋ฅผ ๋ฐํํ๋ค.
private final AtomicLong index = new AtomicLong(0); //์์ฝ์ ์ํ ID
@PostMapping("/reservations")
public ResponseEntity<Reservation> addReservation(@RequestBody Reservation request) {
long id = index.incrementAndGet();
Reservation reservation = new Reservation(id, request.getName(), request.getDate(), request.getTime());
reservations.add(reservation);
URI location = URI.create("/reservations/" + id);
return ResponseEntity.created(location).body(reservation);
}
ํํธ
...
public class ReservationController {
private AtomicLong index = new AtomicLong(1);
...
// AtomicLong ํ์ฉ ์์
reservations.add(new Reservation(index.incrementAndGet(), "๋ธ๋ผ์ด", "2023-01-01", "10:00"));
...
์์ฝ ์ ๋ณด์ ์๋ณ์๋ฅผ ์์ฑํ ๋ AtomicLong๋ฅผ ํ์ฉํ ์ ์๋ค๋ ํํธ๋ฅผ ๋ฐ์๋ค.
incrementAndGet(), decrementAndGet(), getAndIncrement(), getAndDecrement()long id = index.incrementAndGet();
id ์ฆ๊ฐ์ ์ํด์ incrementAndGet๋ฅผ ์ฌ์ฉํ๋ค. ๋ฉ์๋๋ AtomicLong์ ๊ฐ์ 1 ์ฆ๊ฐ์ํค๊ณ , ์ฆ๊ฐ๋ ๊ฐ์ ๋ฐํํฉ๋๋ค. ๋ฐ๋ผ์ index ๋ณ์์ ๊ฐ์ 1์ฉ ์ฆ๊ฐ์ํค๋ฉด์ ๊ทธ ๊ฐ์ id ๋ณ์์ ํ ๋นํ๋ค.
์ฒ์์ id๋ฅผ ๊ทธ๋ฅ int๋ก ์ ์ธํ์๋๋ฐ AtomicLong์ ๋ํด์ ํ์ต์ ํ๋ long์ผ๋ก ์ ์ธํด์ผํ๋ค๋ ๊ฒ์ ๊นจ๋ฌ์ ํ์ ์์ ํ๋ค.
reservations.add(reservation);
jvaa Collection์์ ๋ฆฌ์คํธ์ ์ ๋ณด์ถ๊ฐ๊ฐ ๊ฐ๋ฅํ๋ค.
ArrayList์ add()๋ฅผ ์ฌ์ฉํ๋ฉด ๋๋ค. ์ญ์ ๋ ๋ง์ฐฌ๊ฐ์ง์ด๋ค.
@DeleteMapping("/reservations/{id}")
public ResponseEntity<Void> cancelReservation(@PathVariable long id) {
Reservation reservation = reservations.stream()
.filter(it -> Objects.equals(it.getId(), id))
.findFirst()
.orElseThrow(IllegalArgumentException::new);
reservations.remove(reservation);
return ResponseEntity.noContent().build(); // ์ฑ๊ณต(204) ์ํ ์ฝ๋ ๋ฐํ
}
reservations.remove(reservation);
ArrayList์ remove()๋ฅผ ํตํด์ ๋ฐ๋ก ์ญ์ ๊ฐ ๊ฐ๋ฅํ๋ค.
ResponseEntity.noContent().build();
์ฑ๊ณต์ ์ผ๋ก ์์ฝ์ด ์ญ์ ๋๋ฉด 204 ์ฝ๋๋ฅผ ๋ฐํํ๋ค. ์ญ์ ์ฒ๋ฆฌ๊ฐ ์ ์์ ์ผ๋ก ๋์์ผ๋ ์๋ตํ๋ ๋ณธ๋ฌธ์ ์๋ค.
์๊ตฌ ์ฌํญ
InvalidRequest notfoundํํธ
@ExceptionHandler(NotFoundReservationException.class)
public ResponseEntity handleException(NotFoundReservationException e) {
return ResponseEntity.badRequest().build();
}
ํํธ๋ก ์ฃผ์ด์ง @ExceptionHandler๋ฅผ ์ฌ์ฉํด์ ์์ธ์ฒ๋ฆฌ๋ฅผ ์งํํ์๋ค.
package roomescape;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import roomescape.exception.InvalidRequestException;
@ControllerAdvice
public class ReservationExceptionHandler {
@ExceptionHandler(InvalidRequestException.class)
public ResponseEntity handleInvalidRequestException(InvalidRequestException e) {
return ResponseEntity.badRequest().build();
}
}
package roomescape.exception;
public class InvalidRequestException extends RuntimeException {
public InvalidRequestException(String message) {
super(message);
}
}
package roomescape.exception;
public class NotFoundReservationException extends ReservationException {
public NotFoundReservationException(String message) {
super(message);
}
}
private final String INVALID_NAME_REQUEST_MESSAGE = "์ด๋ฆ ์ ๋ณด๊ฐ ๊ณต๋ฐฑ์
๋๋ค.";
private final String INVALID_DATE_REQUEST_MESSAGE = "๋ ์ง ์ ๋ณด๊ฐ ๊ณต๋ฐฑ์
๋๋ค.";
private final String INVALID_TIME_REQUEST_MESSAGE = "์๊ฐ ์ ๋ณด๊ฐ ๊ณต๋ฐฑ์
๋๋ค.";
๋ฆฌ๋ทฐ๋ฅผ ๋ฐ์ ์ดํ ๊ฐ ์์ธ ๋ฌธ๊ตฌ๋ค์ ์์์ฒ๋ฆฌ๋ฅผ ์งํํด์ฃผ์๋ค.
๋ณ๋๋ก class๋ฅผ ์์ฑํ ํ ์ฌ์ฉํด๋ ์ข์ ๊ฒ ๊ฐ๋ค.
๋ฌธ๊ตฌ๋ค์ ์์์ฒ๋ฆฌ๋ฅผ ํ๋ฉด ์ฝ๋ ์ค๋ณต์ด ์ค์ด๋ค๊ณ , ์ผ๊ด์ฑ์ ๊ฐ์ง ์ ์๋ค๋ ์ฅ์ ์ด ์๋ค.
roomescape
ใดcontroller
ใด homeController
ใด reservationController
ใดdomain
ใด Reservation
ใดexception
ใดInvalidRequestException
ใดNotFoundReservationException
ใดReservationException
ใดReservationExceptionHandler
์ฐ์ ๊ธฐ๋ฅ๋ง์ผ๋ก๋ ๋ฏ์ค์ด ๊ตฌ์กฐ์ ๋ํ ํฐ ๊ณ ๋ฏผ์์ด ์์ ๊ฐ์ด ์งํํ์๋ค. DTO ๊ตฌ์กฐ๋ฅผ ๋ค๋ค ํ์ฉํ๊ธธ๋ ์ฐพ์๋ณด๊ฒ ๋์๋ค.
Repository, Controller, Service, DTO์ ๋ํด์ ์๊ฐํด๋ณด๊ณ ๋ ์์ง๋ง ์์ง ์ดํด ํ์ง ๋ชปํ๋ค...
๊ทธ๋์ Spring ๊ตฌ์กฐ์ ๋ํด์ ์ข ๋ ๊ณ ๋ฏผํด๋ณด๊ณ service์ Repository๋ฅผ ํ์ฉํด์ ๋ค์ ๋ฏธ์
์ ์ ์ฉํด๋ณด๊ณ ์ถ๋ค.
+) ๋ฏธ์
์ ์งํํ๋ฉด์ pr์ ๋ณด๋๋๋ xํ์๊ฐ ๋ ์ ์ฐ์ ์ ๋๋์์๋ค.
์๊ณ ๋ณด๋ ์์ฃผ ๊ธฐ๋ณธ์ ์ธ ๊ฑธ ์๊ณ ์์๋ค.

pr๋ณด๋ด๋ branch๋ฅผ base:main์ผ๋ก ์ค์ ํด๋์๊ธฐ ๋๋ฌธ์ mergeํ ์ ์๋ ๋ฌธ์ ์๋ค. base:day024๋ก ๋ณ๊ฒฝํ๋ค.

์ด์ ์ถฉ๋์ด ์๋ ์ํ๋ก ๋ฐ๋์๋ค!

[Spring] Cannot resolve symbol'springframework'
Long ๊ณผ AtomicLong์ ์ด๋ค ์ฐจ์ด๊ฐ ์์๊น?
[JAVA] Atomic ๋ณ์์ ๋์์ฑ ์ ์ด (AtomicInteger, AtomicLong, AtomicBoolean, AtomicReference)
[Spring] ์์ฒญ ๋ฐฉ์(@RequestMapping, @GetMapping, @PostMapping)
Spring Controller ์ด๋
ธํ
์ด์
๊ณผ ํ๋ผ๋ฏธํฐ ํ์
[์คํ๋ง๋ถํธ] @ExceptionHandler๋ฅผ ํตํ ์์ธ์ฒ๋ฆฌ
[Spring] DTO๋ฅผ ์ฌ์ฉํ๋์ด์
[Spring] ์คํ๋ง ํจํค์ง ๊ตฌ์กฐ, DAO, DTO, Entity, Repository ์ ๋ํ์ฌ, DTO๋ฅผ ์ฌ์ฉํ๋ ์ด์
[SpringMVC] SpringMVC ๊ตฌ์กฐ