티스토리 뷰

거래소 검색에 이어 캐릭터 정보 검색하는 기능 구현에 대해 알아보겠습니다. 전체소스는 GitHub를 참고해 주세요.

 

1. 컨트롤러 추가

@RestController
@RequestMapping("/api")
@RequiredArgsConstructor
public class ApiController {

  private final ApiService _apiService;

  ...
  
  /**
   * 캐릭터 기본정보 조회
   */
  @PostMapping("/character/search")
  public ResponseEntity<CharacterRes> characterSearch(@RequestBody CharacterReq params){
    return _apiService.characterSearch(params);
  }


}



2. DTO 구현

@Getter
@Setter
public class CharacterReq {
  private String characterName;
}
@Getter
@Setter
public class CharacterRes {
  private List<CharacterInfo> characterInfo;
}
@Getter
@Setter
public class CharacterInfo {
  @JsonProperty("ServerName")
  private String serverName;

  @JsonProperty("CharacterName")
  private String characterName;

  @JsonProperty("CharacterLevel")
  private Integer characterLevel;

  @JsonProperty("CharacterClassName")
  private String characterClassName;

  @JsonProperty("ItemAvgLevel")
  private String itemAvgLevel;

  @JsonProperty("ItemMaxLevel")
  private String itemMaxLevel;

  private ArmoryProfile armoryProfile;

  @Getter
  @Setter
  public static class ArmoryProfile {
    @JsonProperty("CharacterImage")
    private String characterImage;

    @JsonProperty("ExpeditionLevel")
    private Integer expeditionLevel;

    @JsonProperty("TownName")
    private String townName;

    @JsonProperty("TotalSkillPoint")
    private Integer totalSkillPoint;
  }
}



3. 서비스 구현

@Service
public class ApiService {

  @Value("${loa.apiKey}")
  private String apiKey;

  @Value("${loa.apiUrl}")
  private String apiUrl;

  HttpHeaders headers;
  RestTemplate restTemplate;

  @PostConstruct
  public void init() {
    headers = new HttpHeaders();
    headers.set("Accept", "application/json");
    headers.set("Authorization", "bearer " + apiKey);
    restTemplate = new RestTemplate();
  }
  
  ...
  
  /**
   * 캐릭터 기본정보 조회(GET)
   */
  public ResponseEntity<CharacterRes> characterSearch(CharacterReq params) {
    ResponseEntity<String> jsonResult = restTemplate.exchange(
      apiUrl + "/characters/" + params.getCharacterName() + "/siblings",
      HttpMethod.GET,
      _getEntity(null),
      String.class);

    List<CharacterInfo> characterInfo = _getArrayData(jsonResult, CharacterInfo.class);
    if(characterInfo == null){
      return new ResponseEntity<>(HttpStatus.NOT_FOUND);
    }

    for(CharacterInfo info : characterInfo){
      String name = info.getCharacterName();
      CharacterInfo.ArmoryProfile armoryProfile = restTemplate.exchange(
        apiUrl + "/armories/characters/" + name + "/profiles",
        HttpMethod.GET,
        _getEntity(null),
        CharacterInfo.ArmoryProfile.class).getBody();

      info.setArmoryProfile(armoryProfile);
    }

    CharacterRes res = new CharacterRes();
    res.setCharacterInfo(characterInfo);

    return new ResponseEntity<>(res, HttpStatus.OK);
  }

  /**
   * HttpEntity 획득
   */
  private HttpEntity _getEntity(Object obj) {
    return obj == null ? new HttpEntity<>(headers) : new HttpEntity<>(obj, headers);
  }

  /**
   * JsonArray 데이터 파싱
   */
  private <T> List<T> _getArrayData(ResponseEntity<String> str, Class<T> clazz) {
    String jsonArray = str.getBody();
    ObjectMapper objectMapper = new ObjectMapper();

    try {
      return objectMapper.readValue(jsonArray, objectMapper.getTypeFactory().constructCollectionType(List.class, clazz));
    } catch (IOException e) {
      e.printStackTrace();
      return null;
    }
  }
}



4. 화면 구현

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<th:block th:replace="~{fragments/head.html :: headFragment}"/>
<body>
<th:block th:replace="~{fragments/top.html :: topFragment}"/>
<main>
  <div class="search-container">
    <label for="characterName">캐릭터 이름:</label>
    <input type="text" id="characterName" placeholder="캐릭터 이름 입력" onkeydown="javascript: if(event.keyCode==13){ searchCharacters(); return false; }">
    <button onclick="searchCharacters()">검색</button>
  </div>

  <div id="characterList" class="character-list"></div>
</main>
<script th:inline="javascript">
  function searchCharacters() {
    let characterName = document.getElementById('characterName').value;
    let data = { characterName: characterName };

    postApi('/api/character/search', data, onSearchCharactersSuccess, onApiError);
  }

  function onSearchCharactersSuccess(data) {
    const characterList = document.getElementById('characterList');
    characterList.innerHTML = '';

    data.characterInfo.forEach((character, index) => {
      const characterDiv = document.createElement('div');
      characterDiv.className = 'character';

      var profileCheck = character.armoryProfile;

      if(profileCheck){
        characterDiv.innerHTML = `
        <div class="accordion" onclick="toggleAccordion(${index})">
          <h3>${character.CharacterName} (${character.CharacterClassName})</h3>
        </div>
        <div class="panel" id="panel${index}">
          <img class="profile-img" src="${character.armoryProfile.CharacterImage}" alt="${character.armoryProfile.CharacterName} 이미지">
          <p><strong>서버:</strong> ${character.ServerName}</p>
          <p><strong>아이템 레벨:</strong> ${character.ItemAvgLevel}</p>
          <p><strong>캐릭터 레벨:</strong> ${character.CharacterLevel}</p>
          <p><strong>원정대 레벨:</strong> ${character.armoryProfile.ExpeditionLevel}</p>
        </div>
      `;
      } else {
        characterDiv.innerHTML = `
        <div class="accordion" onclick="toggleAccordion(${index})">
          <h3>${character.CharacterName} (${character.CharacterClassName})</h3>
        </div>
        <div class="panel" id="panel${index}">
          <img class="profile-img" src="/images/default.png" alt="이미지">
          <p><strong>서버:</strong> ${character.ServerName}</p>
          <p><strong>아이템 레벨:</strong> ${character.ItemAvgLevel}</p>
          <p><strong>캐릭터 레벨:</strong> ${character.CharacterLevel}</p>
        </div>
      `;
      }

      characterList.appendChild(characterDiv);
    });
  }

  function toggleAccordion(index) {
    const panel = document.getElementById(`panel${index}`);
    panel.style.display = panel.style.display === 'block' ? 'none' : 'block';
  }

</script>
</body>
</html>



5. 실행화면

캐릭터검색



감사합니다.

최근에 올라온 글
Total
Today
Yesterday