๐ REST API
๐ RestController
๐ PathVariable
๐ DTO
๐ ResponseEntity
๐ @Autowired / @Qualifier
REST๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ํ๋ API (Application Programming Interface)
HTTP ์ฌ์ฉ ํ์ค๋ฒ์ผ๋ก ์ ์๋ ์ดํ๋ฆฌ์ผ์ด์
์ํคํ
์ฒ. ์์์ ํํ์ผ๋ก์จ(์์์ URI๋ก ํํํ๋ ํํ๋ก ์์ฒญํ์ฌ) ๊ทธ ์์์ ์ํ๋ฅผ ์ฃผ๊ณ ๋ฐ๋๋ค.
๐ฆ REST์ ๊ตฌ์ฑ
์์(RESOURCE)
์ดํ๋ฆฌ์ผ์ด์
์ด ๋ค๋ฃจ๋ ๋ฐ์ดํฐ, ๊ฐ์ฒด ๋ฑ์ ๊ฒ
๋ค๋ฅธ ์์๊ณผ ๊ตฌ๋ณ๋๋ ๊ณ ์ ํ HTTP URI๋ฅผ ๊ฐ์ง๋ค.
ํํ(Representations)
์์์ ์ํ ์์ฒญ์ ๋ํ ์๋ฒ์ ์๋ต
๋ฐ์ดํฐ ์์ฒด๋ฅผ ์๋ต์ผ๋ก ์ ๊ณตํจ (JSON, XML, TEXT ... ๋ฑ์ ํํ)
ํ์(Verb)
HTTP METHOD (CRUD : GET / POST / PUT / DELETE)
์ฆ, ๋ฐ์ดํฐ๊ฐ (CRUD) ์์ฒญ๋ ๋ ๊ณ ์ ํ HTTP URI๋ฅผ ๊ฐ์ง ๋ฆฌ์์ค๊ฐ ์ฒ๋ฆฌ๋์ด ์๋ต์ผ๋ก ์ ๊ณต๋๋ฉฐ ์์์ ์ํ๋ฅผ ์ ๋ฌํจ
๐ฆ REST์ ํน์ง
REST๋ฅผ ๊ธฐ๋ฐ์ผ๋ก URI๋ฅผ ํตํด ๋ฆฌ์์ค๋ฅผ ์ฒ๋ฆฌํ๊ฒ ๋๋ Controller
RestController ์์๋ URI๋ก ๋ฆฌ์์ค๋ฅผ ํํํ์ฌ ์ฒ๋ฆฌํ๊ฒ ๋จ.
โก๏ธ URI ๋ฅผ ๋ณ์๋ก ์ฒ๋ฆฌํ ์ ์๊ฒ ํด์ฃผ๋ ๊ฒ @pathvariable ์ด๋
ธํ
์ด์
๋งคํ ์ฃผ์์ {๋ณ์๋ช
}์ ํฌํจํ์ฌ URI๋ฅผ ์ง์ ํด์ฃผ๊ณ ,
๋งคํ ์ฃผ์์ ๋ฃ์ ๋ฉ์๋ ํ๋ผ๋ฏธํฐ์ @pathvariable์ ์์ฑํด์ค๋ค.
๊ณ์ธต ๊ฐ(Controller, View, Business Layer)์ ๋ฐ์ดํฐ ๊ตํ์ ์ํด ์ฌ์ฉํ๋ ๊ฐ์ฒด
Controller๋ Model์ ๋ฐ์ดํฐ๋ฅผ ๋ด์ View๋ก ์ ๋ฌํ๋ค.
Model์ ๋ฐ์ดํฐ๋ฅผ ๋ด์ ๋, DTO๋ก์ ์ ๋ฌํ์ฌ Model๊ณผ View ์ฌ์ด์ ์์กด์ฑ์ ํด์ฒดํด ์ฃผ๋ ๊ฒ์ด๋ค.
RestController์์๋ DTO๋ฅผ ์ด์ฉํด GET, POST, UPDATE, DELETE ์์ฒญ์ ์ํํด์ฃผ๋ ๊ฒ์ด๋ค.
RequestEntity<T>์ ResponseEntity<T>๋ ์คํ๋ง์์ ์ ๊ณตํ๋ HttpEnityt<T> ํด๋์ค๋ฅผ ์์ํ๋ค.
โป HttpEnityt<T> ํด๋์ค๋ ์์ฒญ / ์๋ต์ HttpHeader์ HttpBody๋ฅผ ๊ฐ์ง.
์ฌ์ง์ฒ๋ผ RequestEntity๋ก ์์ฒญ์ ๋ฐ์์์ ์คํ๋ง RestApi๋ฅผ ํตํด ์์ฒญ ์ฒ๋ฆฌ ํ ResponseEntity ๋ก ์๋ตํด์ฃผ๋ ์
๐ฆ ResponseEntity<T>
ํด๋ผ์ด์ธํธ์ HttpRequest ์์ฒญ ์
HttpStatus(์ํ ์ฝ๋), HttpHeaders, HttpBody ์๋ต ๋ฐ์ดํฐ ๋ฅผ ํฌํจํ๋ ํด๋์ค์ด๋ค.
@ResponseBody ์ด๋ ธํ ์ด์ ์ ์ฌ์ฉํด Body๋ฅผ ๋ณด๋๋ ๊ฒ์ ์ถ๊ฐ์ ์ผ๋ก ์ํ ์ฝ๋์ ํค๋ ์ ๋ณด๋ฅผ ์๋ตํ ์ ์๋ค.
@RestController
public class ResponseEntityController {
@GetMapping("/api/v1/entity/data1")
public ResponseEntity<?> getData() { // โก๏ธbody , status ๋ง ์๋ต
return new ResponseEntity<String>("ResponseEntity ์๋ต", HttpStatus.OK);
}
@GetMapping("/api/v1/entity/data2")
public ResponseEntity<?> getData2() { // โก๏ธheader , status๋ง ์๋ต
MultiValueMap<String, String> headers = new HttpHeaders();
headers.add("test-token1", UUID.randomUUID().toString());
headers.add("test-token2", UUID.randomUUID().toString());
headers.add("test-token3", UUID.randomUUID().toString());
return new ResponseEntity<String>(headers, HttpStatus.OK);
}
@GetMapping("/api/v1/entity/data3")
public ResponseEntity<?> getData3() { // โก๏ธbody, header , status ์๋ต
MultiValueMap<String, String> headers = new HttpHeaders();
headers.add("test-token1", UUID.randomUUID().toString());
headers.add("test-token2", UUID.randomUUID().toString());
headers.add("test-token3", UUID.randomUUID().toString());
return new ResponseEntity<String>("test", headers, HttpStatus.OK);
}
@GetMapping("/api/v1/entity/data4")
public ResponseEntity<?> getData4() { // โก๏ธbody ์ DTO๋ก์จ ์๋ตํจ
MultiValueMap<String, String> headers = new HttpHeaders();
headers.add("test-token1", UUID.randomUUID().toString());
headers.add("test-token2", UUID.randomUUID().toString());
headers.add("test-token3", UUID.randomUUID().toString());
return new ResponseEntity<>( // โญ Constructor ์ฌ์ฉ
new CMRespDto<>(1, "์ ์ก ์ฑ๊ณต", "ํ
์คํธ ๋ฐ์ดํฐ"),
headers,
HttpStatus.OK);
}
@GetMapping("/api/v1/entity/data5")
public ResponseEntity<?> getData5() {
โHttpHeaders headers = new HttpHeaders();
headers.add("token1", "aaaa1111");
return ResponseEntity // โญ Builder ์ฌ์ฉ (๊ถ์ฅ)
.ok()
.headers(headers)
.body(new CMRespDto<>(1, "๋ฉ์ธ์ง", "test"));
}
@AllArgsConstructor
โญ@Getter
public class CMRespDto<T> {
private int code; // 1 : ์ฑ๊ณต, -1 : ์คํจ
private String msg; // commit ๋ฉ์ธ์ง
private T data; // ์๋ต ๋ฐ์ดํฐ
}
Response
์คํ๋ง์์๋ ์๋ตํ MediaType์ ์๋ณํ๊ณ , ํด๋น MediaType์ผ๋ก ์๋ต์ ๋ง๋ค์ด์ค ๋ DTO์ Getter๋ก ๊ฐ์ ๊ฐ์ ธ์จ๋ค.
๋ฐ๋ผ์ ResponseEntity์ Body์ ๋ฃ์ด์ค DTO ๊ฐ์ฒด์๋ @Getter ์ด๋
ธํ
์ด์
์ ๊ผญ ๋ฌ์์ค๋ค !
โ Request ์ผ ๋๋ ..
Ajax๋ฅผ ํตํด formData ํํ๋ก ๋ฐ์ดํฐ๋ฅผ ๋ฐ์์ค๋ฉด, ์คํ๋ง์ด HTML ํ์ผ ์์ formData ํํ์์ DTO ๊ฐ์ฒด๋ก ๋ณํํ์ฌ ๊ฐ์ ธ์ค๋๋ก ํ๋ค. ๋ฐ๋ผ์ ์ด ๊ฒฝ์ฐ์๋ DTO ๊ฐ์ฒด์ @Getter ๊ฐ ํ์ํ๋ค.
์ด๊ฑธ ๊น๋จน๊ณ ์ ์๋๋ ํ์ ์ง์ง (ฮฆ็ฟฮฆ)
์คํ๋ง ์์๋ ์คํ๋ง IoC ์ปจํ ์ด๋์ ์ฌ์ฉํ๋ ๋น(์์กด์ฑ์ ๊ฐ์ง๋ ๊ฐ์ฒด)๋ค์ ์ฃผ์ ํ์ฌ ์ฌ์ฉํ๊ฒ ๋๋ค.
application.xml ์ bean ๋ฑ๋ก์ ํด์ฃผ์ด์ผ ํจ.
ํน์ ์ปจํ
์ด๋์ ๋ฑ๋กํ ๋น ํด๋์ค์ โญ@Component ์ด๋
ธํ
์ด์
์ถ๊ฐํ๋ฉด ์คํ๋ง์์ ์๋์ผ๋ก ๋น ๋ฑ๋ก์ ํด์ค๋ค.
<@Component ๋ฅผ ์์ํ๋ ์ด๋
ธํ
์ด์
: ์๋์ผ๋ก ๋น ๋ฑ๋ก ๋จ>
@Service โก๏ธ ๋น์ฆ๋์ค ๋ก์ง์ ์ฒ๋ฆฌํ๋ ์๋น์ค ํด๋์ค์ ๋ฌ์์ค๋ค.
@Repository โก๏ธ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ฐ๊ฒฐํ๋ DAO ํด๋์ค์ ๋ฌ์์ค๋ค.
@Controller โก๏ธ ์ฌ์ฉ์ ์์ฒญ์ ์ ์ดํ๋ ์ปจํธ๋กค๋ฌ ํด๋์ค์ ๋ฌ์์ค๋ค.
โก๏ธ ์์กด์ฑ์ ๊ฐ์ง๋(์คํ๋ง ์ปจํ ์ด๋์์ ๊ฐ์ ธ์ฌ) ๋ถ๋ถ์ @Autowired ์ด๋ ธํ ์ด์ ์ ๋ฌ์์ ์๋์ผ๋ก ์์กด์ฑ ์ฃผ์ ๋ฐ์ ์ ์๋ค.
์คํ๋ง ์ปจํ ์ด๋์ ๊ฐ์ ํ์ ์ ๋น์ 2๊ฐ ์ด์ ๋ฑ๋กํ๋ ค๊ณ ํ๋ค๋ฉด ๋น์ ์ด๋ฆ ๊ฐ(์๋ณ์)์ ์ฃผ๊ณ @Qualifier ์ด๋ ธํ ์ด์ ์ ์ฌ์ฉํ์ฌ ์ฌ์ฉํ ์์กด ๊ฐ์ฒด๋ฅผ ์ ํํด์ผ ํ๋ค.
โฝ UserRestController.java
@Slf4j
@RestController
@RequestMapping("/api/v1")
public class UserRestController {
@Autowired
@Qualifier("a") // โญ a๋ก ์ง์ ๋ Repository๋ฅผ ์ฌ์ฉํ๊ฒ ๋ค๋ ๋ป
private UserRepository userRepository;
@GetMapping("/users/{userCode}") // path variable
public ResponseEntity<?> getUser(@PathVariable int userCode) {
User user = userRepository.findUserByUserCode(userCode);
return ResponseEntity
.ok()
.body(user);
// โญ ๋น๋ ํจํด์ ์ฌ์ฉํ์ฌ ResponseEntity์
// Status์ OK ์ํ ์ฝ๋, Body์ user ๋ฐ์ดํฐ๋ฅผ ๋ด์ ๋ฆฌํดํจ.
}
@PostMapping("/user")
public ResponseEntity<?> addUser(UserAddReqDto userAddReqDto) {
int result = userRepository.save(userAddReqDto.toEntity());
if(result == 0) { // save() ๋ฆฌํด๊ฐ์ด 0์ด๋ฉด ์คํจ
return ResponseEntity
.internalServerError()
.body("๋ฐ์ดํฐ ์ค๋ฅ(Server)");
}
return ResponseEntity
.ok()
.body("์ฌ์ฉ์ ์ถ๊ฐ ์๋ฃ");
}
}
ํด๋์ค์ @RequestMapping ์ ์ค์ ํ URI๋ ๋ฉ์๋์ @Get โข Post Mapping ์ ์ค URI๊ฐ ํฉ์ณ์ง๋ค.
@GetMapping ๋งคํ ์ฃผ์ โก๏ธ http://localhost:8000/api/v1/users/1 ์ด ๋๋ ๊ฒ์.
โฝ UserAddReqDto.java
@Data
public class UserAddReqDto {
private String userId;
private String userPassword;
private String userName;
private String userEmail;
public User toEntity() { // Dto๋ก ์ ๋ณด๋ฅผ ๋ฐ์์ User๋ก ๋ฐ๊พธ์ด์ค ๊ฒ์.
return User.builder() // User ; user_code, ... ์ ๊ฐ์ง
.user_id(userId)
.user_password(userPassword)
.user_name(userName)
.user_email(userEmail)
.build();
}
}
โฝ UserRepositoryImpl.java
@Repository("a") // โญ ์ด ํด๋์ค๊ฐ ์ด๋ฆ(์๋ณ์) a๋ก ์ปจํ
์ด๋์ ๋ฑ๋ก๋จ.
public class UserRepositoryImpl implements UserRepository{
private final List<User> userData;
public UserRepositoryImpl() { // ์์ฑ์
userData = new ArrayList<User>();
for(int i = 0; i < 5; i++) { // DB ์์ฑ ์ user1 ~ user5 ๊น์ง ๋ง๋ค์ด ๋ .
int index = i + 1;
User user = User.builder()
.user_code(index)
.user_id("user" + index)
.user_name("1111")
.user_password("user" + index)
.user_email("user" + index + "@")
.build();
userData.add(user);
}
}
/* ๐ซ save(user)
* userData ๋ฅผ ๋๊น์ง ๋ฐ๋ณตํ์ฌ
* ๋ ํฐ userCode๊ฐ ๋์ฌ ๋๋ง๋ค maxCode๋ฅผ ๊ฐฑ์ ํ๋ค.
* ๊ฐ์ฅ ํฐ maxCode ๊ฐ์ 1์ ๋ํด์
* ์ ์ฅํ user ์ ๋ณด์ userCode ๊ฐ์ผ๋ก ์ค์ ํด์ค ๊ฒ
*/
@Override
public int save(User user) {
try {
int maxCode = 0;
for(User userObj : userData){
if(userObj.getUser_code() > maxCode) {
maxCode = userObj.getUser_code();
}
}
maxCode++;
user.setUser_code(maxCode);
userData.add(user); // userData์ user ์ถ๊ฐํจ.
}catch (Exception e) {
return 0; // ์คํจ
}
return 1; // ์ฑ๊ณต
}
@Override
public User findUserByUserCode(int userCode) {
User user = null;
for(User userObj: userData) {
if(userObj.getUser_code() == userCode) {
user = userObj;
}
}
return user;
}
}
- ์ฐธ๊ณ ๐ง
REST API ์ ๋๋ก ์๊ณ ์ฌ์ฉํ๊ธฐ
REST API๋? REST, RESTful์ด๋?
์ค๋์ @pathvariable ์ด๋
ธํ
์ด์
!
ResponseEntity, HttpEntity๋?
@ResponseBody ์ฌ์ฉํ ๋ ๊ฐ์ฒด์ getter๊ฐ ์์ ๊ฒฝ์ฐ
@Autowired ๋ถ์!
@Qualifier ์ด๋
ธํ
์ด์
์ฌ์ฉ๋ฒ
Java Bean VS Spring Bean
Using ResponseEntity in Spring
๐ข ์๊ฐ ๐
์๋ฒฝ์ด๋ค. ๋ฐฐ๊ฐ ๊ณ ํ๋ค๐ฅบ