25. 업적 달성 판정과 동시 다발 이벤트 집계
난이도 중 해설 보기 →
결함을 모두 찾고 원인·수정안·더 나은 설계를 제시하라. 마커
(A)(B) 는 주목 위치 힌트다.
결함 코드 · C#
// ============================================================================
// [코드리뷰 문제] C# - 업적 달성 판정과 동시 다발 이벤트 집계
// ----------------------------------------------------------------------------
// 시나리오 (콘텐츠/게임로직 · 서버-클라):
// - 업적은 누적 카운터에 기반한다. 예: "몬스터 100마리 처치", "골드 1,000,000 누적 획득".
// - 전투/획득 이벤트는 여러 워커 스레드에서 동시에 발생한다(몹 처치, 골드 드롭 등).
// 각 이벤트가 해당 업적의 진행도(progress)를 증가시킨다.
// - 진행도가 목표치(threshold)에 도달하면 그 업적은 "달성" 으로 1회만 처리되고,
// 보상(타이틀/재화/아이템)을 정확히 한 번만 지급한다.
//
// 요구사항:
// - 동시 이벤트에서도 진행도 증가가 유실되면 안 된다(정확한 누적).
// - 업적 달성/보상 지급은 정확히 한 번만 일어나야 한다(이중 지급 금지).
// - 이미 달성한 업적은 추가 이벤트가 들어와도 다시 보상하지 않는다.
//
// 과제:
// 이 코드의 잠재적 문제를 모두 찾고, 왜/어떻게 진행도 유실·이중 달성·보상 중복이
// 동시 인터리빙으로 발생하는지 설명하고, 수정안과 더 나은 설계를 제시하라.
// (먼저 직접 리뷰를 적은 뒤 answer.md 와 대조할 것)
// ============================================================================
using System;
using System.Collections.Generic;
public class Achievement
{
public int AchievementId;
public long Threshold; // 목표치
public long Progress; // 누적 진행도
public bool Unlocked; // 달성 여부
}
public class AchievementService
{
// playerId -> (achievementId -> Achievement)
private readonly Dictionary<long, Dictionary<int, Achievement>> _byPlayer = new();
private readonly Action<long, int> _grantReward; // (playerId, achievementId)
public AchievementService(Action<long, int> grantReward)
{
_grantReward = grantReward;
}
// 여러 워커 스레드에서 동시 호출: 이벤트가 진행도를 amount 만큼 증가
public void OnProgress(long playerId, int achievementId, long amount)
{
if (!_byPlayer.TryGetValue(playerId, out var map)) return;
if (!map.TryGetValue(achievementId, out var ach)) return;
if (ach.Unlocked) return; // 이미 달성했으면 무시
// (A) 진행도 누적
ach.Progress += amount;
// (B) 목표 도달 시 달성 처리 + 보상
if (ach.Progress >= ach.Threshold)
{
ach.Unlocked = true;
_grantReward(playerId, achievementId);
}
}
// 신규 업적 등록(플레이어 로그인 시 등)
public void Register(long playerId, Achievement ach)
{
if (!_byPlayer.TryGetValue(playerId, out var map))
{
map = new Dictionary<int, Achievement>();
_byPlayer[playerId] = map;
}
// (C) 동시 등록/조회와의 경합 고려 없음
map[ach.AchievementId] = ach;
}
} 결함 코드 · C++
// ============================================================================
// [코드리뷰 문제] C++ - 업적 달성 판정과 동시 다발 이벤트 집계
// ----------------------------------------------------------------------------
// 시나리오 (콘텐츠/게임로직 · 서버-클라):
// - 업적은 누적 카운터에 기반한다. 예: "몬스터 100마리 처치", "골드 1,000,000 누적".
// - 전투/획득 이벤트가 여러 워커 스레드에서 동시에 발생해 진행도(progress)를 증가시킨다.
// - 진행도가 목표치(threshold)에 도달하면 업적은 1회만 달성 처리되고, 보상을 정확히
// 한 번만 지급한다.
//
// 요구사항:
// - 동시 이벤트에서도 진행도 증가가 유실되면 안 된다(정확한 누적).
// - 업적 달성/보상 지급은 정확히 한 번만(이중 지급 금지).
// - 이미 달성한 업적은 추가 이벤트가 들어와도 다시 보상하지 않는다.
//
// 과제:
// 이 코드의 잠재적 문제를 모두 찾고, 왜/어떻게 진행도 유실·이중 달성·보상 중복·자료구조
// 손상이 동시 인터리빙으로 발생하는지 설명하고, 수정안과 더 나은 설계를 제시하라.
// (먼저 직접 리뷰를 적은 뒤 answer.md 와 대조할 것)
// ============================================================================
#include <cstdint>
#include <unordered_map>
#include <functional>
struct Achievement {
int achievementId;
int64_t threshold; // 목표치
int64_t progress; // 누적 진행도
bool unlocked; // 달성 여부
};
class AchievementService {
public:
explicit AchievementService(std::function<void(int64_t, int)> grantReward)
: grantReward_(std::move(grantReward)) {}
// 여러 워커 스레드에서 동시 호출
void onProgress(int64_t playerId, int achievementId, int64_t amount) {
auto pit = byPlayer_.find(playerId);
if (pit == byPlayer_.end()) return;
auto& map = pit->second;
auto ait = map.find(achievementId);
if (ait == map.end()) return;
Achievement& ach = ait->second;
if (ach.unlocked) return; // 이미 달성했으면 무시
// (A) 진행도 누적
ach.progress += amount;
// (B) 목표 도달 시 달성 + 보상
if (ach.progress >= ach.threshold) {
ach.unlocked = true;
grantReward_(playerId, achievementId);
}
}
// 신규 업적 등록(로그인 시 등)
void registerAch(int64_t playerId, const Achievement& ach) {
// (C) 동시 등록/조회와의 경합 고려 없음
byPlayer_[playerId][ach.achievementId] = ach;
}
private:
std::unordered_map<int64_t, std::unordered_map<int, Achievement>> byPlayer_;
std::function<void(int64_t, int)> grantReward_;
}; 내 리뷰 · C#
내 답안 · 자동 저장
작성 후 위 해설 보기에서 모범 해설과 대조하세요.
내 리뷰 · C++
내 답안 · 자동 저장
작성 후 위 해설 보기에서 모범 해설과 대조하세요.