간단한 토이 프로젝트 준비 중 RESTful API에 대해 알게 되어 정리해보았다.
📍 API 란?
API란 Application Programming Interface의 약자로 프로그램을 실행하는 인터페이스이다.
API는 서버와 데이터베이스에 대한 출입구 역할을 한다
📍 REST API 란?
Representational State Transfer
REST API란 REST Architecture의 제약 조건을 준수하는 애플리케이션 프로그래밍 인터페이스를 뜻한다.
자원을 이름(자원의 표현)으로 구분해 해당 자원의 상태(정보)를 주고 받는 것을 의미한다.
= 자원(Resource)의 표현(Representation)에 의한 상태 전달
📍 RESTful API에서 HTTP Method
- GET : 조회 (받겠다)
- POST : 리소스 생성 (보내겠다)
- PUT : 리소스 전체 갱신(놓겠다/넣겠다)
- DELETE : 리소스 삭제 (지정한 서버의 파일을 삭제하겠다)
📍 예제
작업중이였던 로스트아크 API로 웹 토이 프로젝트 준비중이 였던 코드 중 내 캐릭터 정보를 불러오는 코드가 있었다.
package MH.domain.lostark;
@Controller
@RequiredArgsConstructor
public class LostarkController {
private final LostarkMarketItemService marketItemService;
private final LostarkContentAvgService contentAvgService;
private final LostarkCharacterApiService characterApiService;
@GetMapping("/lostark/character")
public String character(Model model) {
String mainCharacter = "마볼링";
JSONArray character = characterApiService.Characters(mainCharacter);
String mainServer = "루페온";
ArrayList characters = new ArrayList();
for (Object object : character) {
JSONObject jsonObject = (JSONObject) object;
if (jsonObject.get("ServerName").equals(mainServer)) {
JSONObject characterName = characterApiService.characterProfiles((String) jsonObject.get("CharacterName"));
LostarkCharacterProfileDto dto = new LostarkCharacterProfileDto();
dto.setCharacterName((String) jsonObject.get("CharacterName"));
dto.setCharacterImage((String) characterName.get("CharacterImage"));
dto.setCharacterClassName((String) characterName.get("CharacterClassName"));
dto.setItemMaxLevel(characterName.get("ItemMaxLevel"));
dto = setContent(dto);
characters.add(dto);
}
}
// dto안에 ItemMaxLevel순으로 정렬
Comparator<LostarkCharacterProfileDto> itemMaxLevelComparator = Comparator.comparingDouble(dto -> dto.getItemMaxLevel());
characters.sort(itemMaxLevelComparator.reversed());
model.addAttribute("characters", characters);
model.addAttribute("mainCharacter", mainCharacter);
model.addAttribute("mainServer", mainServer);
return "lostark/lostarkCharacter";
}
private LostarkCharacterProfileDto setContent(LostarkCharacterProfileDto dto) {
double level = dto.getItemMaxLevel();
if (level < 1415) {
dto.setChaosName("X");
dto.setGuardianName("X");
} else if (level >= 1415 && level < 1445) {
dto.setChaosName("타락 1");
dto.setGuardianName("데스칼루다");
} else if (level >= 1445 && level < 1460) {
dto.setChaosName("타락 2");
dto.setGuardianName("데스칼루다");
} else if (level >= 1460 && level < 1475) {
dto.setChaosName("타락 2");
dto.setGuardianName("쿤겔라니움");
} else if (level >= 1475 && level < 1490) {
dto.setChaosName("타락 3");
dto.setGuardianName("쿤겔라니움");
} else if (level >= 1490 && level < 1520) {
dto.setChaosName("공허 1");
dto.setGuardianName("칼엘리고스");
} else if (level >= 1520 && level < 1540) {
dto.setChaosName("공허 2");
dto.setGuardianName("칼엘리고스");
} else if (level >= 1540 && level < 1560) {
dto.setChaosName("절망 1");
dto.setGuardianName("하누마탄");
} else if (level >= 1560 && level < 1580) {
dto.setChaosName("절망 2");
dto.setGuardianName("하누마탄");
} else if (level >= 1580 && level < 1600) {
dto.setChaosName("천공 1");
dto.setGuardianName("소나벨");
} else if (level >= 1600 && level < 1610) {
dto.setChaosName("천공 2");
dto.setGuardianName("소나벨");
} else if (level >= 1610) {
dto.setChaosName("계몽 1");
dto.setGuardianName("가르가디스");
}
return dto;
}
}
평범한 Spring 코드로 주로 플레이하는 서버와 캐릭터 이름으로 캐릭터 정보를 불러와서 프론트쪽에 넘겨주는 메소드이다.
이거와 똑같은 기능을 RESTful Api로 바꾸어 보았다.
@RestController
@RequiredArgsConstructor
public class LostarkApiController {
private final LostarkMarketApiService lostarkMarketApiService;
private final LostarkMarketItemService lostarkMarketItemService;
private final LostarkCharacterApiService characterApiService;
@GetMapping("/lostark/api/saveMarketData/{categoryCode}")
public JSONArray saveMarketData(@PathVariable int categoryCode) {
JSONArray result = lostarkMarketApiService.getMarketCategories(categoryCode);
result.forEach((data) -> {
LostarkMarketItemDto lostarkMarketItemDto = new LostarkMarketItemDto((JSONObject) data);
lostarkMarketItemService.save(lostarkMarketItemDto);
});
return result;
}
@GetMapping("/lostark/api/getMyCharacter")
public ArrayList character(Model model) {
String mainCharacter = "마볼링";
JSONArray character = characterApiService.Characters(mainCharacter);
String mainServer = "루페온";
ArrayList characters = new ArrayList();
for (Object object : character) {
JSONObject jsonObject = (JSONObject) object;
if (jsonObject.get("ServerName").equals(mainServer)) {
JSONObject characterName = characterApiService.characterProfiles((String) jsonObject.get("CharacterName"));
LostarkCharacterProfileDto dto = new LostarkCharacterProfileDto();
dto.setCharacterName((String) jsonObject.get("CharacterName"));
dto.setCharacterImage((String) characterName.get("CharacterImage"));
dto.setCharacterClassName((String) characterName.get("CharacterClassName"));
dto.setItemMaxLevel(characterName.get("ItemMaxLevel"));
dto = setContent(dto);
characters.add(dto);
}
}
// dto안에 ItemMaxLevel순으로 정렬
Comparator<LostarkCharacterProfileDto> itemMaxLevelComparator = Comparator.comparingDouble(dto -> dto.getItemMaxLevel());
characters.sort(itemMaxLevelComparator.reversed());
return characters;
}
private LostarkCharacterProfileDto setContent(LostarkCharacterProfileDto dto) {
double level = dto.getItemMaxLevel();
if (level < 1415) {
dto.setChaosName("X");
dto.setGuardianName("X");
} else if (level >= 1415 && level < 1445) {
dto.setChaosName("타락 1");
dto.setGuardianName("데스칼루다");
} else if (level >= 1445 && level < 1460) {
dto.setChaosName("타락 2");
dto.setGuardianName("데스칼루다");
} else if (level >= 1460 && level < 1475) {
dto.setChaosName("타락 2");
dto.setGuardianName("쿤겔라니움");
} else if (level >= 1475 && level < 1490) {
dto.setChaosName("타락 3");
dto.setGuardianName("쿤겔라니움");
} else if (level >= 1490 && level < 1520) {
dto.setChaosName("공허 1");
dto.setGuardianName("칼엘리고스");
} else if (level >= 1520 && level < 1540) {
dto.setChaosName("공허 2");
dto.setGuardianName("칼엘리고스");
} else if (level >= 1540 && level < 1560) {
dto.setChaosName("절망 1");
dto.setGuardianName("하누마탄");
} else if (level >= 1560 && level < 1580) {
dto.setChaosName("절망 2");
dto.setGuardianName("하누마탄");
} else if (level >= 1580 && level < 1600) {
dto.setChaosName("천공 1");
dto.setGuardianName("소나벨");
} else if (level >= 1600 && level < 1610) {
dto.setChaosName("천공 2");
dto.setGuardianName("소나벨");
} else if (level >= 1610) {
dto.setChaosName("계몽 1");
dto.setGuardianName("가르가디스");
}
return dto;
}
}
내용물인 코드는 변한건 없고 출력 방식과 Controller에 있는 어노테이션이 다르다. (Controller -> RestController)
그리고 최근에 안 사실이지만, RESTful API의 주소도 좋지않은 주소다...
📍 RESTful API 설계 가이드
- 마지막에 / 포함하지 않는다
- _(underbar) 대신 -(dash)를 사용한다.
- 소문자를 사용한다.
- 행위(method)는 URL에 포함하지 않는다.
즉, 위에 코드에서 주소창에 get이 있으면 안된다.