Skip to content

Latest commit

ย 

History

History
180 lines (135 loc) ยท 5.94 KB

2021-09-11-Riot_Summoner_Match_Api.md

File metadata and controls

180 lines (135 loc) ยท 5.94 KB

์†Œํ™˜์‚ฌ ์ „์  ๊ฒ€์ƒ‰ Api๋งŒ๋“ค๊ธฐ

Api๋ฅผ ๋งŒ๋“  ๊ณผ์ •์— ๋Œ€ํ•œ ๊ธ€์ž…๋‹ˆ๋‹ค.  
๋ฌธ์ œ๊ฐ€ ๋ณด์ธ๋‹ค๋ฉด ๋ฐ”๋กœ๋ฐ”๋กœ ๋ฆฌ๋ทฐ ๋ถ€ํƒ๋“œ๋ฆฝ๋‹ˆ๋‹ค!  

์ผ๋‹จ Api๋ฅผ ๋งŒ๋“ค๊ธฐ ์ „์— ํ•ด๋‹น Api์— ๋Œ€ํ•œ ๋””์ž์ธ์„ ๊ฐ„๋‹จํ•˜๊ฒŒ ๋งŒ๋“ค์—ˆ๋‹ค.
๋””์ž์ธ์„ ์ƒ๊ฐํ•˜์ง€ ์•Š๊ณ  ๋งŒ๋“œ๋‹ˆ ๋‚ด๊ฐ€ ๋งŒ๋“ค๊ณ ์ž ํ•˜๋Š” Api๊ฐ€ ์–ด๋–ป๊ฒŒ ์‚ฌ์šฉ๋˜๋Š”์ง€ ๊ฐ์ด ์•ˆ์™”๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

<์Šน๋ฆฌ๊ฐ€ ๋งŽ์€ ๋ถ€๋ถ„์„ ์ž˜๋ผ๋‚ธ๊ฒƒ์ด ์•„๋‹ˆ๋ผ ํ‰์†Œ ์‹ค๋ ฅ์ด ์ข‹์•„์„œ ์Šน๋ฆฌ๊ฐ€ ๋งŽ์€๊ฒ๋‹ˆ๋‹ค>

์ผ๋‹จ ์†Œํ™˜์‚ฌ๋ฅผ ๊ฒ€์ƒ‰ ํ•˜๋ฉด ๋ฌด์กฐ๊ฑด ์ „์ ๋ฆฌ์ŠคํŠธ ๋˜ํ•œ ๊ฐ€์ ธ์˜ค๋„๋ก ํ•˜์ž๊ณ  ์ƒ๊ฐ ํ–ˆ๊ธฐ ๋•Œ๋ฌธ์—

์†Œํ™˜์‚ฌ ๊ฐ์ฒด๊ฐ€ ์ „์ ๋ฆฌ์Šคํ‹€ ๊ฐ€์ ธ์•ผ ํ•œ๋‹ค๊ณ  ์ƒ๊ฐํ–ˆ๋‹ค.

์ผ๋‹จ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๊ณ 

@SpringBootTest
class SummonerServiceTest {
    @Autowired
    SummonerService summonerService;

    @Test
    void save() {
        String name = "ํฐ๊ณ ๋ชจ๋ถ€";

        Summoner summoner = summonerService.getSummoner(name);

        assertThat(summoner.getSummonerName()).isEqualTo("ํฐ๊ณ ๋ชจ๋ถ€");
    }
}

์†Œํ™˜์‚ฌ ์ •๋ณด์™€ ์†Œํ™˜์‚ฌ ์ „์  Entity๋ฅผ ๋งŒ๋“ค์—ˆ๋‹ค.

@Entity
@Data
public class Summoner {
    @Id @GeneratedValue
    @Column(name = "summoner_id")
    private long id;

    private String summonerName;

    private String puuid;

    private int profileIcon;

    @OneToMany(cascade = CascadeType.ALL)
    @JoinColumn(name = "summoner_id")
    private List<MatchReference> matchReferences;

    public Summoner(String summonerName, String puuid, int profileIcon, List<MatchReference> matchReferences) {
        this.summonerName = summonerName;
        this.puuid = puuid;
        this.profileIcon = profileIcon;
        this.matchReferences = matchReferences;
    }

    public Summoner() {

    }

    public static Summoner of(SummonerDto summonerDto, List<MatchReference> matchReferences) {
        return new Summoner(summonerDto.getName(), summonerDto.getPuuid(), summonerDto.getProfileIconId(), matchReferences);
    }
}

@Entity
public class MatchReference {
    @Id @GeneratedValue
    @Column(name = "match_id")
    private long matchId;

    private long gameId;

    private String role;

    private int championId;

    private String lane;

    private Date timestamp;

    @ManyToOne(cascade = CascadeType.ALL)
    @JoinColumn(name = "summoner_id", insertable = false, updatable = false)
    private Summoner summoner;

    public MatchReference(long gameId, String role, int championId, String lane) {
        this.gameId = gameId;
        this.role = role;
        this.championId = championId;
        this.lane = lane;
    }

    public MatchReference() {

    }

    public static List<MatchReference> of(MatchListDto matchListDto) {
        List<MatchReferenceDto> matchReferenceDtos = matchListDto.getMatches();
        return matchReferenceDtos.stream()
                .map(e -> new MatchReference(e.getGameId(), e.getRole(), e.getChampion(), e.getLane()))
                .collect(Collectors.toList());
    }
}

์†Œํ™˜์‚ฌ ์ •๋ณด๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ๋Š” Entity์™€ ์†Œํ™˜์‚ฌ ์ „์ ๋ฆฌ์ŠคํŠธ์— ๋Œ€ํ•œ ์ •๋ณด๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ๋Š” Entity๋ฅผ ๋งŒ๋“ค์—ˆ๋‹ค.
์ž๋ฐ” ORM ํ‘œ์ค€ JPA ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์ฑ…์—์„œ ์ผ๋Œ€๋‹ค ์–‘๋ฐฉํ–ฅ Mapping์„ ๋ณด๊ณ  ์„ค์ • ํ–ˆ๋Š”๋ฐ.
์‚ฌ์‹ค ์ƒ๊ฐํ•ด๋ณด๋ฉด ์ผ๋Œ€๋‹ค ๋‹จ๋ฐฉํ–ฅ Mapping์ด ๋งž๋‹ค๊ณ  ์ƒ๊ฐ์ด ๋“ค์—ˆ์ง€๋งŒ ์ผ๋Œ€๋‹ค ๋‹จ๋ฐฉํ–ฅ Mapping๋ณด๋‹จ ๋‹ค๋Œ€์ผ ์–‘๋ฐฉํ–ฅMapping์„ ํ•˜๋ผ๊ณ  ์ ํ˜€ ์žˆ์—ˆ๋‹ค.
๊ทธ๋Ÿฐ๋ฐ ํ•œ๋ช…์˜ ์†Œํ™˜์‚ฌ ๊ฐ€ ์—ฌ๋Ÿฌ ์ „์  ๋ฆฌ์ŠคํŠธ๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ๊ณ  ์ฃผ์ฒด๊ฐ€ ์†Œํ™˜์‚ฌ ์ด๊ธฐ ๋•Œ๋ฌธ์— ์ผ๋Œ€๋‹ค ๊ฐ€ ๋งž๋‹ค๊ณ  ์ƒ๊ฐํ–ˆ๊ณ , ์ผ๋Œ€๋‹ค ๋‹จ๋ฐฉํ–ฅ์„ ์ถ”์ฒœํ•˜์ง€ ์•Š์œผ๋‹ˆ ์–‘๋ฐฉํ–ฅ์œผ๋กœ ์ผ๋‹จ ์„ค์ •ํ•ด๋†“๊ณ , JPA๋ฅผ ์ข€ ๋” ์•Œ์•„๋ณด๊ณ  ์ˆ˜์ • ํ•ด์•ผ ๊ฒ ๋‹ค๊ณ  ์ƒ๊ฐ ํ–ˆ๋‹ค.

Controller, Service, Repository๋ฅผ ๋งŒ๋“ค์—ˆ๋‹ค.

@RestController
public class SummonerController {
    public final RiotApi riotApi;
    public final SummonerService summonerService;
    
    public SummonerController(RiotApi riotApi, SummonerService summonerService) {
        this.riotApi = riotApi;
        this.summonerService = summonerService;
    }

    @GetMapping("/summoners/{summonerName}")
    public Summoner getSummoner(@PathVariable String summonerName) {
        return summonerService.getSummoner(summonerName);
    }
}

@Service
public class SummonerService {
    private final RiotApi riotApi;
    private final SummonerRepository summonerRepository;

    public SummonerService(RiotApi riotApi, SummonerRepository summonerRepository) {
        this.riotApi = riotApi;
        this.summonerRepository = summonerRepository;
    }

    public Summoner getSummoner(String summonerName) {
        if (summonerRepository.existsBySummonerName(summonerName)) {
            return summonerRepository.findBySummonerName(summonerName);
        }
        SummonerDto summonerDto = riotApi.getSummoner(summonerName);
        MatchListDto matchListDto = riotApi.getMatches(summonerDto.getAccountId());

        return summonerRepository.save(Summoner.of(summonerDto, MatchReference.of(matchListDto)));
    }
}

@Repository
public interface SummonerRepository extends JpaRepository<Summoner, Long> {
    boolean existsBySummonerName(String summonerName);

    Summoner findBySummonerName(String summonerName);
}

๊ทธ๋ฆฌ๊ณ  DB์— ์ž˜ ์ €์žฅ ๋˜๋Š” ๊ฒƒ์„ ํ™•์ธ ํ–ˆ๋‹ค.

ํšŒ๊ณ 

๊ฐ„๋‹จํ•œ Api์ž‘์—…์ด์—ˆ์ง€๋งŒ ์•„์‰ฌ์šด๊ฒƒ์€ ๋งŽ์•˜๋‹ค.
์ •๋ฆฌ ํ•ด๋ณด๋ฉด

  1. Jpa์— ๋Œ€ํ•ด์„œ ์ž˜๋ชจ๋ฅธ๋‹ค. (๊ณต๋ถ€ํ• ๊ฑด ๋งŽ์€๋ฐ ๋„ˆ๋ฌด ์ƒ๊ฐ ์—†์ด ์ง€๋ƒˆ๊ตฌ๋‚˜..)
  2. Rate Limit๋ฅผ ํ•ด๊ฒฐ ํ•˜์ง€ ๋ชปํ–ˆ๋‹ค.

์ด ์„ธ ๋ถ€๋ถ„์ด ์•„์‰ฌ์› ๋‹ค.
ํ˜ผ์ž ํ•˜๋Š”๊ฒƒ์ด ์‰ฝ์ง€ ์•Š๋‹ค๋Š” ๊ฒƒ์„ ์•Œ๊ณ  ์žˆ์—ˆ์ง€๋งŒ.
์ด๋ ‡๊ฒŒ ๊นŒ์ง€ ๋น„ํšจ์œจ์  ์ผ๊ฑฐ๋ผ๊ณ  ์ƒ๊ฐํ•˜์ง€ ๋ชปํ–ˆ๋‹ค.
๋‚ด ์ž์‹ ์—๊ฒŒ ์ข€ ์•„์‰ฌ์šด๊ฒŒ ๋งŽ์•˜๋‹ค.

์ทจ์—…์— ์„ฑ๊ณต ํ–ˆ์ง€๋งŒ ์ทจ์—…์— ์„ฑ๊ณตํ•˜๋Š” ๋™์•ˆ ์ถฉ๋ถ„ํžˆ ๊นŠ๊ฒŒ ์ƒ๊ฐํ•ด๋ณด๊ณ  ๋‚˜๋ฆ„ ๊ฒฌ๊ณ  ํ•˜๊ฒŒ ์„ค๊ณ„ ํ•  ์ˆ˜ ์žˆ์—ˆ์„ ํ…๋ฐ ๊ทธ๋Ÿฌ์ง€ ๋ชปํ•œ๊ฑด ์•„์‰ฌ์šด๊ฒƒ ๊ฐ™๋‹ค.