# 캐릭터 업데이트 수정

## 1. 현재 코드

```java
@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자리 문자열이 보인다.&#x20;

<figure><img src="/files/pQP8o1ixbYXnPdW7y3IS" alt=""><figcaption></figcaption></figure>

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

### 2) 코드 수정

#### UUID 찾기 / 비교 메소드

```java
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));  
}
```

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

```java
// 원정대 검색할 캐릭터 닉네임 찾기  
// 캐릭터 이미지가 있으면서 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가 있는 첫번째 데이터를 찾아낸다.

#### 원정대 업데이트

```java
// 원정대 업데이트  
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. 테스트

#### 변경할 데이터

<div><figure><img src="/files/Hp8mrvlX7bNMw10s5ABh" alt=""><figcaption></figcaption></figure> <figure><img src="/files/jCnYE7SXRkI3wHDIlMqR" alt=""><figcaption></figcaption></figure></div>

#### 업데이트 메소드 실행

<figure><img src="/files/hMtD48yBlSALwy6RCmsO" alt=""><figcaption></figcaption></figure>

#### 결과

<div><figure><img src="/files/0N0XgL6bB6FJLvsePxtS" alt=""><figcaption></figcaption></figure> <figure><img src="/files/vZv3NHEBjWkB8uidwN3W" alt=""><figcaption></figcaption></figure></div>

* 닉네임 자동 변경 : 테스트할캐릭 -> 마볼링
* 캐릭터 추가 : 넷마블볼링 (이미지 url 미존재)
* 캐릭터 업데이트 : 바드볼링
  * 이미지 업데이트
  * 아이템레벨 업데이트
* 메인 캐릭터 닉네임 변경 : 위 닉네임 자동 변경이랑 연동


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.loatodo.com/retrospect/undefined.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
