캐릭터 업데이트 수정

2024.09.24 닉네임 변경으로 인한 캐릭터 업데이트가 안되는 문제 수정

1. 현재 코드

@Transactional  
public void updateCharacterList(String username) {  
    // 1. 회원 조회  
    Member member = memberDao.get(username);  
  
    // 2. 대표캐릭터 조회  
    String mainCharacter = member.getMainCharacterName();  
  
    // 3. 거래소, 콘텐츠 통계 데이터 조회  
    Map<String, Market> contentResource = marketDao.findContentResource();  
    List<DayContent> chaos = contentDao.findDayContent(Category.카오스던전);  
    List<DayContent> guardian = contentDao.findDayContent(Category.가디언토벌);  
  
    // 4. 대표캐릭터 이름으로 로스트아크 오픈 API를 통해 원정대 조회  
    List<CharacterJsonDto> characterJsonDtoList = lostarkCharacterDao.getCharacterJsonDtoList(mainCharacter, member.getApiKey());  
    for (CharacterJsonDto dto : characterJsonDtoList) {  
        // 4-1. 캐릭터 이미지 추가(캐릭터 조회 오픈 API)
dto.setCharacterImage(lostarkCharacterDao.getCharacterImageUrl(dto.getCharacterName(), member.getApiKey()));  
  
        // 4-2. 등록된 캐릭터와 이름같은 데이터 찾기  
        Optional<Character> find = member.getCharacters().stream()  
                .filter(character -> character.getCharacterName().equals(dto.getCharacterName())).findFirst();  
  
        // 4-3. 업데이트된 아이템 레벨을 바탕으로 일일숙제 수익 수정  
        DayTodo dayContent = new DayTodo().createDayContent(chaos, guardian, dto.getItemMaxLevel());  
  
        if (find.isPresent()) { // 4-4.이름 같은게 있으면 업데이트  
            Character character = find.get();  
            updateCharacter(character, dto, dayContent, contentResource);  
        } else { // 4-5. 이름 같은게 없으면 추가  
            Character character = addCharacter(dto, dayContent, member);  
            calculateDayTodo(character, contentResource);  
            member.getCharacters().add(character);  
        }  
    }  
}

문제

  • 메인캐릭터 닉네임이 변경된다면 '4. 대표캐릭터 이름으로 로스트아크 오픈 API를 통해 원정대 조회' 가 정상적으로 처리되지 않음

  • 그외 다른 캐릭터의 닉네임이 변경된다면 새롭게 데이터가 추가되고, 개인이 기존 캐릭터를 삭제 -> 추후 기존 데이터로 쌓은 기록이 삭제됨

2. 수정

1) 이미지 URL에 있는 UUID(추정) 사용

이미지를 불러왔을 때 URL을 보면 끝에 UUID로 추정되는 64자리 문자열이 보인다.

이것을 이용해서 변경된 캐릭터 닉네임을 판단해서 업데이트를 하려고 한다.

2) 코드 수정

UUID 찾기 / 비교 메소드

public static String getUUID64(String url) {  
    String regex = "([A-Z0-9]{64})";  
    Pattern pattern = Pattern.compile(regex);  
    Matcher matcher = pattern.matcher(url.toUpperCase());  
  
    if (matcher.find()) {  
        return matcher.group(1);  
    } else {  
        throw new IllegalArgumentException("UUID를 찾을 수 없습니다.");  
    }  
}  
  
public static boolean isSameUUID(String url1, String url2) {  
    if (url1 == null || url2 == null) {  
        return false;  
    }  
    return getUUID64(url1).equals(getUUID64(url2));  
}

원정대 검색할 캐릭터 닉네임 찾는 메소드

// 원정대 검색할 캐릭터 닉네임 찾기  
// 캐릭터 이미지가 있으면서 UUID가 일치하는 첫번째 캐릭터  
private String findSiblingCharacterName(Member member) {  
    return member.getCharacters().stream()  
            .filter(character -> character.getCharacterImage() != null)  
            .filter(character -> {  
                CharacterJsonDto updatedCharacter = lostarkCharacterDao.getCharacter(character.getCharacterName(), member.getApiKey());  
                return isMatchingCharacter(character, updatedCharacter);  
            })  
            .map(Character::getCharacterName)  
            .findFirst()  
            .orElseThrow(() -> new IllegalArgumentException(CHARACTER_NOT_FOUND));  
}  
  
  
private boolean isMatchingCharacter(Character character, CharacterJsonDto updatedCharacter) {  
    if (updatedCharacter == null || updatedCharacter.getCharacterImage() == null) {  
        return false;  
    } else {  
        return isSameUUID(updatedCharacter.getCharacterImage(), character.getCharacterImage());  
    }  
}
  • lostarkCharacterDao.getCharacter - 로스트아크 오픈 API로 캐릭터 단건 정보를 가져온다.

  • isMatchingCharacter - 데이터들이 null이 아니고 imageUrl도 있는 데이터 중 같은 UUID가 있는 첫번째 데이터를 찾아낸다.

원정대 업데이트

// 원정대 업데이트  
private void updateSiblings(String searchCharacterName, Member member, List<DayContent> chaos, List<DayContent> guardian,  
                            Map<String, Market> contentResource, String mainCharacter) {  
    List<CharacterJsonDto> siblings = lostarkCharacterDao.getSiblings(searchCharacterName, member.getApiKey());  
  
    siblings.stream()  
            .map(dto -> {  
                // 캐릭터 이미지 업데이트  
                CharacterJsonDto updatedCharacter = lostarkCharacterDao.getCharacter(dto.getCharacterName(), member.getApiKey());  
                if (updatedCharacter.getCharacterImage() != null) {  
                    dto.setCharacterImage(updatedCharacter.getCharacterImage());  
                }  
                return dto;  
            })  
            .forEach(dto -> updateCharacter(dto, member, chaos, guardian, contentResource, mainCharacter));  
}  
  
private void updateCharacter(CharacterJsonDto dto, Member member, List<DayContent> chaos, List<DayContent> guardian,  
                             Map<String, Market> contentResource, String mainCharacter) {  
  
    Optional<Character> matchingCharacter = member.getCharacters().stream()  
            .filter(character -> character.getCharacterImage() != null)  
            .filter(character -> isSameUUID(character.getCharacterImage(), dto.getCharacterImage()))  
            .findFirst();  
  
    DayTodo dayContent = new DayTodo().createDayContent(chaos, guardian, dto.getItemMaxLevel());  
  
    if (matchingCharacter.isPresent()) {  
        // 캐릭터가 존재할 경우 업데이트  
        Character character = matchingCharacter.get();  
        if (character.getCharacterName().equals(mainCharacter)) {  
            member.setMainCharacter(dto.getCharacterName()); // 메인 캐릭터 이름 변경  
        }  
        character.updateCharacter(dto, dayContent, contentResource);  
    } else {  
        // UUID가 일치하지 않으면 이름으로 캐릭터 찾기  
        Optional<Character> characterByName = member.getCharacters().stream()  
                .filter(character -> character.getCharacterName().equals(dto.getCharacterName()))  
                .findFirst();  
  
        if (characterByName.isPresent()) {  
            // 이름으로 찾은 캐릭터가 있으면 업데이트  
            characterByName.get().updateCharacter(dto, dayContent, contentResource);  
        } else {  
            // UUID도, 이름도 일치하는 캐릭터가 없으면 새로 추가  
            Character newCharacter = addCharacter(dto, dayContent, member);  
            calculateDayTodo(newCharacter, contentResource);  
            member.getCharacters().add(newCharacter);  
        }  
    }  
}
  • lostarkCharacterDao.getSiblings - 로스트아크 오픈 API로 원정대 정보를 가져오는데, 여기엔 이미지 URL 값이 없기 때문에 따로 단건 조회 후 저장해준다.

  • UUID가 일치하는 캐릭터가 있으면 업데이트

    • 업데이트하려는 캐릭터가 메인 캐릭터에 저장된 이름이 같으면 같이 업데이트 해준다.

  • UUID가 일치하지 않는 캐릭터가 있다면 두가지 경우가 존재한다.

    • 캐릭터 이미지 URL이 없는 경우 - 이름으로 다시 찾아서 정보를 업데이트 해준다.

    • 둘 다 아니라면, 캐릭터가 추가된 경우이므로 새롭게 생성한다.

3. 테스트

변경할 데이터

업데이트 메소드 실행

결과

  • 닉네임 자동 변경 : 테스트할캐릭 -> 마볼링

  • 캐릭터 추가 : 넷마블볼링 (이미지 url 미존재)

  • 캐릭터 업데이트 : 바드볼링

    • 이미지 업데이트

    • 아이템레벨 업데이트

  • 메인 캐릭터 닉네임 변경 : 위 닉네임 자동 변경이랑 연동

Last updated