55. 캐릭터 이름 변경의 유일성 검사-예약 비원자(중복 이름 발생) · C#
난이도 하 해설 보기 →
결함을 모두 찾고 원인·수정안·더 나은 설계를 제시하라. 마커
(A)(B) 는 주목 위치 힌트다.
결함 코드 · C#
// 시나리오: MMORPG 캐릭터 "이름 변경권" 처리
//
// 플레이어가 이름 변경권을 사용하면 원하는 새 이름으로 캐릭터명을 바꾼다.
// 서버 요구사항:
// - 캐릭터 이름은 월드 전체에서 유일해야 한다(대소문자 무시 정규화 후).
// - 이미 누군가 쓰는 이름으로는 변경할 수 없고, 이 경우 변경권을 소모하지 않는다.
// - 변경에 성공하면 이전 이름은 다시 다른 사람이 쓸 수 있게 풀린다.
// - 이름 변경 요청은 여러 플레이어로부터 스레드 풀에서 동시에 처리된다.
//
// 아래 NameChangeService 는 사용 중인 이름 집합(_taken)과 캐릭터별 현재 이름을 관리한다.
// 같은 미사용 이름 "Aria" 로 두 플레이어가 거의 동시에 변경을 요청하는 상황을 검토하라.
//
// 과제:
// 1) (A)(B) 지점을 중심으로, 동시 요청이 겹칠 때 요구사항이 어떻게 깨지는지 설명하라.
// 2) 재현되는 구체적 인터리빙(시간 순서)을 제시하라.
// 3) 요구사항을 지키도록 수정안을 제시하라.
using System;
using System.Collections.Generic;
public class Character
{
public long Id;
public string Name = "";
public int NameChangeTickets;
}
public class NameChangeService
{
// 현재 사용 중인 이름(정규화된 형태)
private readonly HashSet<string> _taken = new HashSet<string>();
private readonly Dictionary<long, Character> _chars = new Dictionary<long, Character>();
private static string Normalize(string s) => s.Trim().ToLowerInvariant();
public bool TryRename(long charId, string newName)
{
Character c = _chars[charId];
if (c.NameChangeTickets <= 0) return false;
string key = Normalize(newName);
// (A) 이 이름을 지금 아무도 안 쓰는지 확인
if (_taken.Contains(key))
return false; // 이미 사용 중 → 실패, 변경권 보존
// ... 검증/로깅/DB 반영 준비 등 (짧은 처리 시간) ...
// (B) 예약하고 캐릭터에 반영
_taken.Remove(Normalize(c.Name)); // 이전 이름 반납
_taken.Add(key);
c.Name = newName;
c.NameChangeTickets -= 1;
return true;
}
} 결함 코드 · C++
// 시나리오: MMORPG 캐릭터 "이름 변경권" 처리 (C++ 세션/월드 서버)
//
// 플레이어가 이름 변경권을 사용하면 캐릭터명을 새 이름으로 바꾼다.
// 서버 요구사항:
// - 캐릭터 이름은 월드 전체에서 유일해야 한다(정규화 후).
// - 이미 쓰는 이름으로는 변경 불가, 이 경우 변경권 미소모.
// - 성공 시 이전 이름은 다시 사용 가능해진다.
// - 여러 워커 스레드가 이름 변경 요청을 동시에 처리한다.
//
// 같은 미사용 이름으로 두 플레이어가 거의 동시에 요청하는 상황을 검토하라.
//
// 과제:
// 1) (A)(B) 지점을 중심으로 동시 요청 시 요구사항이 어떻게 깨지는지 설명하라.
// 2) 재현되는 인터리빙을 제시하고, 락을 어떻게 잡아야 하는지 논하라.
// 3) 수정안을 제시하라. g++ -std=c++17 -pthread 로 컴파일된다고 가정한다.
#include <string>
#include <unordered_set>
#include <unordered_map>
#include <mutex>
#include <algorithm>
#include <cctype>
struct Character {
long id = 0;
std::string name;
int nameChangeTickets = 0;
};
static std::string normalize(std::string s) {
std::transform(s.begin(), s.end(), s.begin(),
[](unsigned char ch){ return (char)std::tolower(ch); });
return s;
}
class NameChangeService {
public:
bool tryRename(long charId, const std::string& newName) {
Character& c = chars_.at(charId);
if (c.nameChangeTickets <= 0) return false;
std::string key = normalize(newName);
{
// (A) 사용 여부 확인만 잠깐 잠그고 바로 푼다
std::lock_guard<std::mutex> lk(mtx_);
if (taken_.count(key)) return false;
}
// ... 검증/로깅/DB 반영 준비 등 ...
{
// (B) 다시 잠그고 예약·반영
std::lock_guard<std::mutex> lk(mtx_);
taken_.erase(normalize(c.name));
taken_.insert(key);
c.name = newName;
c.nameChangeTickets -= 1;
}
return true;
}
private:
std::mutex mtx_;
std::unordered_set<std::string> taken_;
std::unordered_map<long, Character> chars_;
};
int main() { return 0; } 내 리뷰 · C#
내 답안 · 자동 저장
작성 후 위 해설 보기에서 모범 해설과 대조하세요.
내 리뷰 · C++
내 답안 · 자동 저장
작성 후 위 해설 보기에서 모범 해설과 대조하세요.