안녕하세요

오랜만에 로스트아크 프로젝트를 조금 수정했습니다.

 

기존의 검색 화면인데 정보가 좀 많기도 하고 로아가 조금 변하면서 다른 정보도 좀 추가로 보여줘야겠다 싶어서 화면을 변경하게 됐습니다.

 

변경된 화면인데요 기존의 로스트아크 캐릭터 정보창을 활용해서 그래도 좀 더 친숙하게 보여질 수 있게 했습니다.

 

고대 장비나 악세서리 옵션, 아크패시브 포인트가 꽤나 중요해졌기때문에 좀더 쉽게 확인할 수 있도록 변경했구요

 

일단 이렇게 해서 군장검사를 위한 캐릭터 정보보기 페이지는 마무리를 지을 것 같고 다음으로 만들어둔 탭인 골드정보를 만들어볼까 합니다. 보유 캐릭터별 권장 레이드 라던지 레이드에 따른 골드 획득 정보를 추가로 표시하려고 합니다.

 

감사합니다!

일단 끝이 났습니다. 하지만 끝이 아니에요 쓸때마다 인텔리제이 켜고.. 리액트 실행시키고.. 서버 실행시키고.. 할 수는 없잖아요? 배포를 해서 언제나 사용하도록 하고싶단 말이에요.

 

일단 우리는 가지고 있는 AWS 서버가 있으니 거기에 배포를 해보도록 하겠습니다. 배포하는 과정은 제 예전 글을 보시면 있지만. 저는 한가지 의문이 생겼습니다. 내가 만든 프로젝트는 리액트 + 스프링 부트 인데 얘네 어떻게 같이 패키징 하지?

였어요 개발을 하며 리액트, 서버 따로다로 실행시키고 포트도 달랐기에, 흠.. 인텔리제이가 이것도 똑똑하게 같이 패키징 해주려나? 했지만 그건 아니구요! 이제 패키징 시작해보겠습니다.

 

CORS 설정

예전 글에서 설정했던 MvcConfiguration 파일을 찾아가서 설정값을 변경해줄거에요 아래 '서버ip' 부분을 발급받은 AWS 탄력 IP로 변경해주도록 하겠습니다.

 

jsx 파일 API 경로 수정

리액트 이벤트에서 서버에 API를 요청하는 경로도 이제 localhost에서 변경될 AWS 서버의 ip로 바꿔줍니다.

 

Build.gradle 수정

마지막으로 우리가만든 리액트 소스파일 들이 스프링 부트가 패키징 되며 합쳐져야 합니다. 빌드를 하면서 합쳐질 수 있도록 아래 설정을 추가해주도록 하겠습니다. 맨 처음 frontendDir 변수를 선언할 때 마지막 경로 부분은 본인이 생성하신 작업 폴더를 지정해주셔야해요!

def frontendDir = "$projectDir/src/main/lostark_react"

sourceSets {
	main {
		resources { srcDirs = ["$projectDir/src/main/resources"]
		}
	}
}

processResources { dependsOn "copyReactBuildFiles" }

task installReact(type: Exec) {
	workingDir "$frontendDir"
	inputs.dir "$frontendDir"
	group = BasePlugin.BUILD_GROUP
	if (System.getProperty('os.name').toLowerCase(Locale.ROOT).contains('windows')) {
		commandLine "npm.cmd", "audit", "fix"
		commandLine 'npm.cmd', 'install' }
	else {
		commandLine "npm", "audit", "fix" commandLine 'npm', 'install'
	}
}

task buildReact(type: Exec) {
	dependsOn "installReact"
	workingDir "$frontendDir"
	inputs.dir "$frontendDir"
	group = BasePlugin.BUILD_GROUP
	if (System.getProperty('os.name').toLowerCase(Locale.ROOT).contains('windows')) {
		commandLine "npm.cmd", "run-script", "build"
	} else {
		commandLine "npm", "run-script", "build"
	}
}

task copyReactBuildFiles(type: Copy) {
	dependsOn "buildReact"
	from "$frontendDir/build"
	into "$projectDir/src/main/resources/static"
}

 

gradle을 새로고침하여 적용시켜 주고 jar를 만들어 주세요!

 

생성된 jar를 서버로 옮겨주시고 실행시켜 주시면!

 

이렇게 최종적으로 배포된 프로젝트를 확인할 수 있습니다. 이제 언제 어디서든 군장검색기를 사용할 수 있게됐습니다.

 

그럼 이렇게 로스트아크 프로젝트는 음.. 최종 마무리는 아니구요 상세페이지 라던지 로아를 하면서 불편한 기능이 있다면 다시 만들러 돌아오겠습니다. 감사합니다~

 

엽바!

안녕하세요~~~~~

이제 API 연동부터 데이터 가공까지 과정들을 끝내고 마지막으로 최종 화면에 정보를 출력하는 작업이 남았습니다.

 

 

자 이게 최종 화면인데요 꽤 괜찮은 것 같기두 하구요 ㅋㅋㅋ

화면을 나누고 출력하는 과정은... 사실 잘 모르겠습니다.. 프론트 너무 어려운 것 같아요..

 

음 이정도면 공대장을 하면서 파티원들 군장검사 할 때 한번에 볼 수 있겠죠??  상세 탭을 만드는 건 다음에 하도록 할게요 ㅋㅋㅋ 화면 구역 나누는게 정말 힘들었거든요...ㅜㅜ

 

여기까지 해서 로스트아크 군장검색기를 만들어 봤습니다. 로아를 하면서 유용하게 사용하도록 하겠고요 다음에는 또 다른 개발로 돌아오겠습니다.

 

감사합니다!

 

엽바~~!

 

안녕하세요!

저번시간까지 우리는 필요한 데이터들을 전부 수집했습니다.

하지만 그 데이터들을 전부 표시할것은 아닙니다. 중요한 정보들을 간추리고 계산해서 꼭 필요한 내용들만 출력될 수 있도록 해줘야 해요. 오늘은 그 작업을 해보겠습니다.

 

정보 추출

이제 정보를 추출해야하는데요 한 가지 문제가 생겼습니다. 초월의 정보를 예로 들면 우리는 방어구, 무기에 초월 정보를 가져와야 합니다. 그래서 저는 예상으로는 ArmoryEquipment 라는 방어구 객체에 투구,어깨 등 장비 객체가 들어가 있고 그 안에 "초월" 필드가 있어 그 수치를 가져와야 겠다. 라고 생각했는데요 아래는 초월 정보가 들어있는 문자 입니다.

  "Element_010": {
    "type": "IndentStringGroup",
    "value": {
      "Element_000": {
        "contentStr": {
          "Element_000": {
            "bPoint": false,
            "contentStr": "민첩 +5880"
          },
          "Element_001": {
            "bPoint": false,
            "contentStr": "모든 장비에 적용된 총 126개"
          },
          "Element_002": {
            "bPoint": false,
            "contentStr": "5 - 장착중인 모든 장비의 초월 등급 당 최대 생명력이 80씩, 아군 공격력 강화 효과가 0.01%씩 증가합니다."
          },
          "Element_003": {
            "bPoint": false,
            "contentStr": "10 - 장착중인 모든 장비의 초월 등급 당 힘, 민첩, 지능이 55씩, 아군 공격력 강화 효과가 0.01%씩 증가합니다."
          },
          "Element_004": {
            "bPoint": false,
            "contentStr": "15 - 장착중인 모든 장비의 초월 등급 당 무기 공격력이 14씩, 아군 공격력 강화 효과가 0.01%씩 증가합니다."
          },
          "Element_005": {
            "bPoint": false,
            "contentStr": "20 - 장착중인 모든 장비의 초월 등급 당 공격력이 6씩, 아군 공격력 강화 효과가 0.01%씩 증가합니다."
          }
        },
        "topStr": "슬롯 효과[초월] 7단계 21"
      }
    }
  },

tooltip 이라는 장비 객체 안에 정말 모든 정보가 그냥 문자열 하나로 들어가 있습니다...

이 정보로 초월 단계에 접근하려면 Element_010 > value > Element_000 > topStr 까지 가야 초월 활성화 수치 21을 가져올 수 있습니다.

문제는

Element_010 이 고정된 값이 아니다.

 - 눈치 채신분도 있겠지만 이 문구는 우리가 장비창에서 장비에 마우스를 올렸을 때 보여주는 그 문구들을 전부 문자열로 출력해준것입니다. 그래서 상급재련을 안한 장비는 _010이 009 가 될수도 있고 무기의 경우 에스더 여부에 따라 또 수치가 달라져서 _010이 초월 정보를 가지고 있는 객체라는것을 확신할 수가 없습니다.

 

그래서 저는 정규식을 사용했습니다. 저 전체의 문구에서 topStr에 해당하는 문구는 딱 1개이기 때문에 patter을 사용해서 저 슬롯 효과[초월] 7단계가 있는 문구를 찾고 거기서 활성화 수치를 가져오는 방법으로 선택했습니다.

 

제가 예시로 초월을 들었지만 사실상 거의 모든 수치가 저런 하나의 문자열 tooltip으로 제공되고 그 간단한 강화 수치마저 "+16 화려한 구원의 전장 모자" 여기서 16이라는 수치를 추출해서 사용해야합니다. 

 

API가 조금 불친절한게 아닌가 하는 생각을 했습니다 ㅜ.

 

그래서 이런식으로 특정 고정된 문구를 찾아서 거기서 강화 수치, 등급, 필요 정보들을 뽑아오는것으로 했고 최종적으로 반환할 캐릭터 객체에 정보를 담았습니다.

 

각인 API

이게 진짜 불쾌한 API 였는데요 아크패시브를 개방하신 분들은 직업각인을 이제 각인 말고 깨달음 수치로 사용을 하게 됩니다. 그래서 아크패시브를 개방하지 않은 캐릭터는 각인 정보에 직업각인이 나와서 그냥 사용할 수 있는데,

아크패시브 사용자는 또 하나로 쓰여진 문자열에서 직업각인 정보를 추출해서 사용해야합니다. 근데 짜증나는게 누구는 1티어에 직업각인을 찍을 수 있고 누구는 2티어 부터 직업각인을 사용 가능합니다. 굳이 왜..??? 대체 왜???

그래서 아크패시브에서 직업각인 정보는 그냥 빼버렸습니다 ㅋㅋ

 

이렇게 모든 정보를 가공해서 추출하는 작업을 끝냈습니다.

다음 시간에는 추출한 정보들을 최종적으로 화면에 보여주도록 하겠습니다.

 

감사합니다.

엽바!

안녕하세요~ 

저번 시간까지는 클라이언트에서 캐릭터명을 서버에 전달하고 서버에서는 로스트아크 API를 수행하고 다시 클라이언트에 캐릭터 정보를 전달해 화면에 출력하는, 어느 정도의 기초 뼈대를 잡아봤습니다. 

이제 많은 정보중에서 어떤 정보들을 보여줘야 할것인가를 정리를 해야합니다. 일단 제가 목표로 한것은 첫 검색화면에서는 복잡한 정보들을 빼버리고 정말 한눈에 볼 수 있도록 정보들을 모으고 싶단 말이죠...

그래서 이 정도의 정보로 각각의 정보들을 요약해서 보여주는 작업을 진행하고 상세 탭에서 각각의 정보를 좀 더 자세히 볼 수 있도록 만들겠습니다. 

 

시작하겠습니다!

 

1. VO 생성하기

이제 각각의 데이터들을 받아올 VO를 생성해줘야합니다. 로스트아크 API의 JSON 응답은 양이 좀 많아서 계층구조를 원활하게 파악하기가 어려운데요 그래서 사이트 하나를 추천해드리겠습니다.

 

https://jsonviewer.stack.hu/

 

Online JSON Viewer and Formatter

 

jsonviewer.stack.hu

 

 

사이트에 들어가셔서 이렇게 Text 영역에 Json 응답을 붙여넣기 해주시면

Viewer 탭에서 Json 객체의 구조를 쉽게 파악할 수 있고, 이제 여기서 필요한 데이터들만 뽑아서 VO를 만들어 주겠습니다.

 

자 이렇게 JSON과 매핑될 VO를 전부 생성해줬구요 테스트까지 진행해보면 정상적으로 값들을 가져오는것을 확인할 수 있습니다.

 

이번 시간은 JSON 객체에서 우리가 사용하려는 데이터들을 VO로 생성해서 매핑을 해줬습니다.

다음시간에는 데이터들을 우리가 사용하려는 목적에 맞게 가공해주는 작업을 진행해보겠습니다.

 

감사합니다!

엽바! 

안녕하세요~~

 

저번 시간에는 서버에서 API를 호출하고 필요한 정보들만 뽑아서 클라이언트에 JSON Object로 전달하는 것을 해봤습니다. 오늘은 우리가 받은 JSON Object를 화면에 출력해보도록 할게요

 

1. 라우팅 적용 (화면 전환)

먼저 라우팅을 적용하기 위해 App.js를 변경해줬습니다. 기존에 난잡하게 있던 코드들을 분리 했습니다. 

Header 부분과 Nav 부분을 Header라는 .jsx 파일을 생성해서 분리해줬구요 또 캐릭터의 정보가 출력될 영역을 Search.jsx로 따로 나눠 분리했습니다.

import React, { useState } from 'react';
import './App.css';
import Header from './Header';
import Search from './contents/Search'
import { BrowserRouter, Route, Routes } from "react-router-dom";

function App() {

    const [userData, setUserData] = useState(null);

  return (
      <div className="App">
          <BrowserRouter>
              <Header setUserData={setUserData} />
              <Routes>
                  <Route path='/loa/:characterName' element={<Search userData={userData} setUserData={setUserData}/>} />
              </Routes>
          </BrowserRouter>
      </div>
  );
}

export default App;

 

라우팅 이란 간단하게 페이지를 전환해주는거라고 생각하시면 됩니다. 하나의 페이지 안에서 요청한 url에 따른 정보만 가져와 새로 렌더링하는 방식으로 리액트가 제공하는 리액트 라우터가 있습니다.

 

이를 사용하기위해 react-router-dom의 설치가 필요합니다. 터미널에서 아래 명령어를 실행해서 설치를 진행해주세요

npm install react-router-dom

 

설치가 완료됐다면 import로 BrowserRouter, Routes, Route 이렇게 3개를 추가해주세요.

 

BrowserRouter

BrowserRouter는 앱 전체를 감싸는 라우터로서 브라우저의 히스토리 API를 사용해서 페이지를 가져와 렌더링을 맡아주는 역할로 앱에서 라우팅 기능을 사용할 수 있도록 해주는 컨테이너 라고 생각하시면 됩니다.

Routes

Routes는 Route가 여러개 있을 때 묶어서 관리해주는 컨테이너 입니다.

Route

Route는 특정 URL 경로에 해당하는 컴포넌트를 연결하는 역할입니다.

 

위 App.js를 보게되면 항상 화면 위에 표시될 Header영역이 있고 URL( 캐릭터 검색)에 따라 화면이 다르게 전환돼야 하기 때문에 /loa의 경로는 Search.jsx를 찾아가게 됩니다.

 

<Header setUserData={setUserData} /> 는 저번 시간에 사용했던 state를 사용한 코드인데요 Header.jsx 파일에 setUserData라는 이름으로 우리가 선언한 setUserData useState값을 인자로 전달해준 것입니다.

 

<Route path='/loa/:characterName' element={<Search userData={userData} setUserData={setUserData}/>} /> 를 살펴보면 이 Route는 /loa 라는 URL에 응답할 컴포넌트 이고 :characterName은 우리가 캐릭터를 검색하면 localhost/loa/"캐릭터명" 이런식으로 url에 캐릭터명이 붙을텐데 이를 명시해주는 url입니다.  계속해서 이 Route의 element는 Search이고 위 header처럼 인자로 userData와, setUserData를 전달해줍니다.

 

태그의 Header와 Search는 우리가 최 상단에 import 시켜준 값을 인식하여 파일을 찾아갑니다. 

 

2. 검색 api 수정

jsx?

이번에 파일을 분리하며 .js를 .jsx 파일로 생성했는데요 .jsx는 js문법을 확장한 문법으로 리액트에서 사용이 가능하고, js 파일 안에서 HTML을 같이 작성해서 사용할 수 있도록 지원합니다. 

 

자 이제 수정된 Header.jsx 파일을 확인해 보겠습니다.

import {useState} from "react";
import {Link, useNavigate} from "react-router-dom";

const Header = ({setUserData}) => {

    const [inputValue, setInputValue] = useState('');
    const navigate = useNavigate();

    function getUser(characterName) {
        fetch("http://127.0.0.1:8080/loa/"+ characterName,{
            method: 'GET',
            headers: {
                "Content-Type": "application/json",
            },
        })
            .then(res => res.json())
            .then(data => {
                setUserData(data);
                console.log(data);
                navigate("/loa/"+characterName);
            })
            .catch(err => console.log(err));
    }
    
  중략..
  <div className="container">
                    <Link to="/">민엽 군장검사</Link>
  </div>
  
  export default Header;

 

여기서 변경된 부분은 Link, useNavigate와 fetch url을 조금 수정했습니다.

Navigate

자 우리는 서버로부터 정상적으로 API 응답을 받아왔는데요. 받아오기만 하면 끝이 아니겠죠? 이제 해당 url로 이동해서 화면에 출력해줘야 합니다. 그래서 .then 영역에 navigate를 통해 우리가 Route path로 지정했던 /loa/"캐릭터명" 으로 화면을 전환시켜줄겁니다. 

Link

.jsx에서는 a 태그의 href가 아닌 Link문법을 사용해서 To="경로"로 이동하는 방법을 사용합니다.

 

setUserData

여기에 하나의 추가작업이 들어갔습니다. 처음 const Header 부분을 보면 setUserData로 매개변수가 있습니다. 우리가 App.js에서 setUserData을 인자로 줬던것이 생각나시죠? .then에서 가져온 응답을 사용해서 json 객체를 상위 컴포넌트(App.js)로 전달할 수 있도록 사용했습니다. 다음에 설명하겠지만 새로고침을 했을때 내용을 유지할 수 있도록 상위 컴포넌트에서도 JSON 객체의 정보를 가지고 있을 수 있게 처리한거에요!

 

3. 화면 출력

자 이제 마지막으로 가져온 정보들을 토대로 화면을 만들어 줘야겠죠 분리한 Search.jsx 입니다.

import { useEffect } from "react";
import { useParams } from "react-router-dom";



const Search = ({userData, setUserData }) => {

    const { characterName } = useParams();

    useEffect(() => {
        if (!userData && characterName) {
            fetch("http://127.0.0.1:8080/loa/" + characterName, {
                method: 'GET',
                headers: {
                    "Content-Type": "application/json",
                },
            })
                .then(res => res.json())
                .then(data => setUserData(data))
                .catch(err => console.log(err));
        }
    }, [characterName, userData, setUserData]);

    return (
        <div className="center-container">
            {userData ? (
                <div className="box">
                    <div className="character-info1">
                        {userData.characterName}
                    </div>
                    <div className="character-info2">
                        <div className="character-info3">
                            <img src={userData.characterImage} alt="캐릭터 이미지" className="detail-image" />
                        </div>
                        <div className="character-info4">
                            <dl>
                                <dt>서버</dt>
                                <dd>{userData.serverName}</dd>
                            </dl>
                            <dl>
                                <dt>클래스</dt>
                                <dd>{userData.characterClassName}</dd>
                            </dl>
                            <dl>
                                <dt>원정대</dt>
                                <dd>{userData.expeditionLevel}</dd>
                            </dl>
                            <dl>
                                <dt>아이템 레벨</dt>
                                <dd>{userData.itemMaxLevel}</dd>
                            </dl>
                            <dl>
                                <dt>캐릭터 레벨</dt>
                                <dd>{userData.characterLevel}</dd>
                            </dl>
                            <dl>
                                <dt>칭호</dt>
                                <dd>{userData.title}</dd>
                            </dl>
                        </div>
                    </div>
                </div>
            ) : (
                <div className="box">굿</div>
            )}
        </div>
    );
}
export default Search;

 

화면 구성같은경우는 그냥 무시해주세요.... 정말 못생겼습니다 ㅜㅜ

 

useEffect, useParams

새로고침 작업을 위해 사용했습니다. useParams의 경우 url에 있는 파라미터를 가져오는 명령어 이고 useEffect는 렌더링 후 특정 상태나 값이 변경이될 때 실행하도록 설정하는 것으로 if의 조건문을 실행하는 것인데 [characterName, userData, setUserData]); 부분이 이 if문이 언제 실행될지 적용하는거에요 이 배열안의 값들이 변경될 때마다 실행된다고 생각하시면 됩니다. 그래서 새로고침을 하며 값이 변할때 다시 실행되도록 했습니다.

 

삼항연산자

userData ? (...) : <div>...</div> 이 부분이 보이실텐데요 우리가 가져온 데이터가 값이 없을수도 있는데 그냥 userData의 값을 뽑아서 써버리면 문제가 생기겠죠?? 삼항연산자를 통해 값을 한번 체크해줍시다. 자 그다음 적절히 값들을 배치해주면? 

자 이렇게 우리가 가져온 정보들이 출력된것을 확인할 수 있습니다. 예쁘지 않은건 그냥 넘어가주세요 ㅜㅜ 저는 정말 미적감각이 없어서 정말 저걸 만들면서도 어떻게 배치해야되지...??? 이게 맞아..? 하면서 했답니다..

 

자 오늘은 우리가 서버에서 전송한 정보들을 화면에 가볍게 출력해봤습니다.

이제 좀 더 많은 정보들을 함축적으로 보여줘야겠죠?? 다음은 서버에서 더 많은 데이터를 가공해보도록 하겠습니다.

 

감사합니다!!

엽바!

안녕하세요~

저번 시간에는 로스트아크의 오픈 API를 사용하기위해 키를 발급받고 간단한 테스트로 받아오는것을 해봤습니다.

이번에는 이제 우리의 서버에서 API를 호출하고 그 데이터를 다시 웹으로 전달하는 작업을 해보겠습니다.

 

1.  API 데이터 확인

저번에 API를 테스트해봤을때 보셨겠지만 기본 캐릭터 검색을 하게되면 정말 방대한 정보가 검색됩니다.

그래서 저는 일단 테스트를 위해 profiles의 정보만 가지고 확인해보도록 하겠습니다.

검색해보면 위쪽에 요청 url과 아래 필드 응답값들이 나오구요 페이지 맨 아래쪽으로 내리면 해당 모델의 정보를 확인하실 수 있습니다. 

저기서 필요한 정보들만 가져다 쓰도록 하겠습니다.

 

2. VO 생성

@Getter
@Setter
@JsonIgnoreProperties(ignoreUnknown = true)
public class AromoryProfile {
    private String characterImage; // 캐릭터 사진
    private String expeditionLevel; // 원정대 레벨
    private String title; // 칭호
    private String serverName; // 서버명
    private String characterName; // 캐릭터명
    private String characterClassName; // 클래스명
    private int characterLevel; // 캐릭터 레벨
    private String itemMaxLevel; // 아이템 레벨
}

당장 테스트를 위해 필요하다고 생각하는 요소들만 추가를 해줬습니다. 

@JsonIgnoreProperties 같은 경우 위 Object에서 매칭되지 않는 요소들은 무시해주기 위해서 추가했습니다.

 

3. Service 생성

@Service
public class CharacterService {

    static final String apiKey = "본인 API KEY";
    private AromoryProfile profiles;

    public AromoryProfile getUser(String characterName) throws IOException, InterruptedException {

        characterName = URLEncoder.encode(characterName,"utf-8");

        String url = "https://developer-lostark.game.onstove.com/armories/characters/"+characterName+"/profiles";

        HttpClient client = HttpClient.newHttpClient();

        HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create(url))
                .header("Authorization","Bearer "+apiKey)
                .build();

        HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());

        if(response.statusCode() == 200) {
            String jsonResponse = response.body();

            ObjectMapper objectMapper = new ObjectMapper().configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES,true);
            profiles = objectMapper.readValue(jsonResponse, AromoryProfile.class);


        }else {
            System.out.println(response.statusCode());
        }

        return profiles;

    }
}

 

API를 요청할 service를 생성해줬습니다. 클라이언트에서 전달받은 캐릭터명을 인코딩해주고 URL을 httpClient를 통해 전송하고 로스트아크 서버로부터 JSON 응답을 받아옵니다. 그리고 ObjectMapper를 통해 우리가 생성한 ArmoryProfile의 필드값들과 매핑해줍니다.

 

4. Controller 생성

@RestController
@RequestMapping("/loa")
public class LostarkController {

    @Autowired
    private CharacterService characterService;

    @GetMapping("")
    public AromoryProfile getUser(@RequestParam String characterName) throws IOException, InterruptedException {
        return characterService.getUser(characterName);
    }
}

컨트롤러 에서는 서비스를 호출해주고 VO를 응답값으로 주겠습니다.

 

5. 클라이언트 값 확인

function getUser(characterName) {
  fetch("http://127.0.0.1:8080/loa?characterName=" + characterName,{
    method: 'GET',
    headers: {
      "Content-Type": "application/json",
    },
  })
      .then(res => res.json())
      .then(data => {
        console.log(data);
      })
      .catch(err => console.log(err));
}

당장은 화면에 출력은 못하기때문에 받아온 데이터를 json화 시켜서 콘솔창에서 확인해보도록 하겠습니다.

이렇게 정상적으로 응답을 받아오는것을 확인할 수 있습니다.

 

자 오늘은 클라이언트에서 전송한 데이터를 서버에서 API 요청으로 캐릭터의 정보를 받아오고 또 필요한 정보들만 추려서 다시 클라이언트로 전송하는것까지 진행했습니다. 다음은 요청 받은것들을 화면에 표시하는 작업을 해보겠습니다.

 

오류 더보기

더보기

1. Cannot deserialize value of type java.lang.Character from Object value (token JsonToken.START_OBJECT)

- 받으려는 데이터를 Character로 생각없이 만들어버렸다. ㅋㅋㅋㅋ Character는 Char를 처리하는 클래스이기 때문에 다른것으로 변경해서 사용하도록 하자.

 

2. Unrecognized field "ArmoryProfile" (class com.yeop.lostark.vo.LostarkCharacter), not marked as ignorable 

- 사실 처음에는 전체 데이터를 받아와서 ArmoryProfile만 뽑아오려 했다. 처음 구조는

루트>ArmoryProfile,등등

이런식이었는데 내가 루트를 가져와서 ArmoryProfile만 뽑아내려니 Jackson이 역직렬화를 할 때 루트 클래스가 매핑이 안되다보니 나온 오류였다.

 

2-1. 첫 번째 방법으로 루트에 해당하는 Profiles 객체를 만들고 그 안에 armoryProfile을 필드 객체로 추가해서 받아오는 방법을 생각했다. 최종적으로도 이렇게 가려고한다. 하지만 로스트아크 API가 전달하는 ArmoryProfile과 내가 필드로 넣은 armoryProfile이 대소문자가 일치하지 않아 매핑을 하지 못했다. (정말 귀찮다.) 이럴때는 객체 안에 @JsonProperty("필드명") 으로 이름이 다를경우에 맞춰서 매핑이 가능하다.

 

2-2. 일단은 ArmoryProfile만 받아오자는 생각으로 위 포스팅처럼 진행했다. ArmoryProfile 클래스를 만들고 ArmoryProfile만 제공하는 API로 해당 필드들을 받아오는것으로 했다.

 

3. 필드 대소문자 불일치

2번과 똑같은 오류인데 2-2 방법으로 진행하면서 이번엔 필드값들이 대소문자가 맞지 않는 오류가 발생했다.

그래서 내가 사용하는 ObjectMapper에 대소문자를 구분하지 않고 매핑하도록 설정을 해줬다.

ObjectMapper().configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES,true) 이 옵션으로 가능하다.

 

 

감사합니다!

엽바!

이전 시간에는 프론트 화면에서 서버로 데이터를 전송해봤습니다.

이제 우리는 전송받은 캐릭터 명으로 로스트아크의 API를 이용해서 필요한 정보들을 뽑아내야합니다. 그러기 위해 로스트아크의 API 키를 발급받아야 하는데요

 

로스트아크 API키 발급

1. 아래사이트에 접속해주세요

https://developer-lostark.game.onstove.com/

 

Lostark OpenAPI Developer Portal

Open API For All Developers START BUILDING YOUR OWN CLIENTS TODAY USING OFFICIAL DATA. GET ACCESS TO LOSTARK API

developer-lostark.game.onstove.com


2. 가운데 GET ACCESS TO LOSTARK API 를 클릭해주세요

(Stove 로그인 해주셔야 됩니다.)

 

3. 클라이언트명을 영어로 작성해 주시고 아래 동의사항 체크해주신 뒤 CREATE 해주시면 됩니다.

 

4. API키 복사 

API 요청할 때 필요한 key입니다. 복사해주세요

주의사항으로 분당 100개의 요청이 가능합니다!

 

6. API KEY 승인

상단의 API DOCUMENTS > CHARACTERS로 들어와 주시고 주황색 AUTORIZE 버튼을 클릭해주세요
그 후 아래 bearer이 기본으로 입력돼있습니다. 그 뒤에다가 복사하신 API KEY를 붙여넣어주신 후 인증 받으시면 됩니다.

 

API 테스트

아래 GET 메소드에서 Try it out 눌러주시고

 

Description 빝에 박스가 활성화되면서 캐릭터 이름을 넣을 수 있습니다. 거기에 본인 캐릭터명을 입력해보세요.

 

그 후 Execute를 눌러주시면 아래처럼 JSON 형태로 본인의 캐릭터 정보를 확인할 수 있답니다.

 

자 이제 API 키 발급까지 끝냈는데요 다음시간에는 우리 서버에서 직접 데이터를 요청해서 받아보도록 하겠습니다.

 

감사합니다.

 

엽바!

+ Recent posts