Project GB PokéTeam Week 3

포켓몬 팀 빌더 GB PokéTeam 개발 3주차 #GitFlow #JsonDeserialize #Setter

Project GB PokéTeam Week 3
Photo by Thimo Pedersen / Unsplash

들어가며

이제 본격적으로 기능 구현을 시작합니다.

Branch 관리

GitHub - Godbell/GB-PokeTeam-API: Pokémon Team Build Support Tool
Pokémon Team Build Support Tool. Contribute to Godbell/GB-PokeTeam-API development by creating an account on GitHub.

현재 PokéTeam API의 PokéDex 기능을 개발 중입니다. 핵심 기능들을 PokéDex와 PokéTeam Builder, PokéTeam Share 총 세 가지로 크게 분류하였고 각 분류에 해당하는 엔드포인트를 개발할 때 그에 맞는 Branch를 만들어 개발하려고 합니다. 최종적으로 예상하는 구성은 다음과 같습니다.

main
└─ hotfix
└─ develop
└── endpoint-pokedex
└── endpoint-builder
└── endpoint-share

Endpoint 구현

api/v1/pokedex/pokemon/{idOrName}을 통해 특정 포켓몬의 정보를 불러옵니다. 이를 위해 Spring Boot 서버에서 PokéAPI로부터 포켓몬 정보를 받아옵니다. 기본적인 포켓몬의 스탯 정보를 가져올 수 있도록 DTO를 더 작성했습니다.

@Data
@JsonIgnoreProperties(ignoreUnknown = true)
public class Pokemon
{
    /**
     * basic data
     */
    public int id;
    public String name;
    public List<PokedexNumber> pokedex_numbers;

    /**
     * physical data
     */
    public Double weight;
    public Double height;
    public void setWeight(Object weight)
    {
        this.weight = ((Integer)weight).doubleValue() / 10.0;
    }
    public void setHeight(Object height)
    {
        this.height = ((Integer)height).doubleValue() / 10.0;
    }

    /**
     * statical datas
     */
    @JsonProperty("types")
    @JsonDeserialize(using = PokemonTypeDeserializer.class)
    public List<String> types;
    public PokemonStat[] stats;
}

이 중 포켓몬의 지방별 도감번호를 저장하는 pokedex_numberspokeapi.co/api/v1/pokemon-species에서, 나머지는 pokeapi.co/api/v1/pokemon에서 가져옵니다. PokéAPI에서 제공하는 형태 그대로 사용하는 데이터도 있지만, 그렇지 않은 데이터도 있습니다.

weight와 height

포켓몬의 체중과 신장을 나타내는 값입니다. PokéAPI에서는 각각 Decimeter, Hectogram단위로 제공하며 자료형은 Integer입니다. 일반적으로 사용하는 단위인 미터와 킬로그램에 맞추기 위해서 자료형을 Double로 바꾸고 10을 나누려고 합니다. 해당 Attribute에 대응하는 Setter 메서드를 정의하면 받아온 데이터를 역직렬화할 때 호출됩니다. 기본적으로 Integer 값이 들어오므로 형 변환과 함께 데이터를 담습니다.

public Double weight;
public Double height;

public void setWeight(Object weight)
{
    this.weight = ((Integer)weight).doubleValue() / 10.0;
}
public void setHeight(Object height)
{
    this.height = ((Integer)height).doubleValue() / 10.0;
}

types

포켓몬의 타입을 나타냅니다. 포켓몬은 단일 타입이거나 제 2타입을 가질 수 있습니다. PokéAPI에서 받아오는 정보는 다음과 같은 형태입니다.

"types": [
  {
    "slot": 1,
    "type": {
      "name": "fairy",
      "url": "https://pokeapi.co/api/v2/type/18/"
    }
  }
]

저는 이걸 다음과 같은 형태로 만들어 저장하려고 합니다.

"types": ["fairy"]

즉 데이터 역직렬화 과정에서 type 필드 내부 JSON 접근해 name 필드의 값을 받아오는 작업이 필요합니다. Setter 선언만으로 해결할 수 없으니 Deserializer를 새로 정의합니다.

public class PokemonTypeDeserializer extends JsonDeserializer<List<String>> 
{
    @Override
    public List<String> deserialize(
        JsonParser jsonParser, 
        DeserializationContext ctxt
    )   throws IOException 
    {
        JsonNode node;
        List<String> types = new ArrayList<>();
        try
        {
            node = jsonParser.getCodec().readTree(jsonParser);

            for (JsonNode typeNode : node) {
                JsonNode nameNode = typeNode.get("type").get("name");
                String name = nameNode.asText();
                types.add(name);
            }
        }
        catch (IOException e)
        {
            throw new IOException("Error parsing JSON: " + e.getMessage());
        }
        return types;
    }
}

타입명을 저장할 ArrayList 하나를 선언한 뒤 받아온 JSON 데이터에서 아까 언급한 것처럼 name 필드의 값을 받아옵니다.

이걸 이제 @JsonDeserialize Annotation의 매개 변수로 넣어 주면 됩니다.

@JsonProperty("types")
@JsonDeserialize(using = PokemonTypeDeserializer.class)
public List<String> types;

데이터 받아오기

잘 받아와지네요!