Day 10 — 오늘 배운 게임 서버 개발 지식 5가지
학습일: 2026년 11월 11일
복습일: 2026년 11월 12일
주제: C# 컬렉션 메모리, Prefix Sum, 디자인 패턴, 동시 구매 처리, PostgreSQL 기초
게임 서버 개발을 위한 압축 학습 10일차다.
오늘은 자료구조의 메모리 배치, 구간 합 알고리즘, 디자인 패턴, 동시 구매 처리, 관계형 데이터베이스를 공부했다.
특히 이번 복습에서는 개념을 이해했다고 생각했지만 실제 코드에 적용하면서 틀리는 부분이 있었다.
이번 학습에서 중요하게 느낀 것은 알고리즘의 시간 복잡도뿐 아니라 메모리 사용량, 데이터 정합성, 실제 구현 정확성까지 함께 고려해야 한다는 점이다.
1. 자료구조 — Struct와 Class의 메모리 구조
C#에서 struct와 class는 각각 값 형식(Value Type)과 참조 형식(Reference Type)이다.
이 차이는 컬렉션에 데이터를 저장하는 방식과 실제 성능에도 영향을 줄 수 있다.
List<Struct>와 List<Class>의 차이
다음 두 타입을 생각해 보자.
public struct PlayerStruct
{
public int Level;
public int Gold;
}
public class PlayerClass
{
public int Level;
public int Gold;
}
각 타입의 데이터를 List에 저장한다.
List<PlayerStruct> listA = new();
List<PlayerClass> listB = new();
두 List는 모두 내부적으로 배열을 사용하지만, 배열에 저장되는 데이터는 다르다.
List<PlayerStruct>
구조체가 값 형식이므로 내부 배열의 각 요소에 구조체 값 자체가 저장된다.
따라서 배열에 저장된 데이터를 순차적으로 접근할 때 연속적인 메모리 접근의 이점을 얻을 수 있다.
List<PlayerClass>
클래스는 참조 형식이므로 내부 배열에는 PlayerClass 객체 자체가 아니라 객체에 대한 참조가 저장된다.
실제 PlayerClass 객체들은 별도로 할당되므로 메모리에 연속적으로 위치한다는 보장이 없다.
CPU 캐시 지역성이 중요한 이유
CPU는 메모리에서 데이터를 읽을 때 인접한 데이터도 함께 캐시에 가져올 수 있다.
순차적으로 필요한 데이터가 인접해 있다면 캐시에 적재된 데이터를 활용할 가능성이 높아진다.
반면 객체 참조를 따라 여러 메모리 위치에 접근하면 캐시 효율이 불리할 수 있다.
따라서 전체 순회가 모두 O(N)이더라도 실제 실행 속도에는 차이가 발생할 수 있다.
하지만 struct가 항상 class보다 빠르다는 뜻은 아니다.
구조체가 크면 복사 비용이 증가할 수 있으며, 실제 접근 패턴과 객체 구성에 따라서도 성능이 달라진다.
게임 서버에서도 데이터의 크기, 변경 방식, 순회 빈도 등을 고려해 타입과 자료구조를 선택해야 한다.
2. 알고리즘 — Prefix Sum과 Sliding Window 비교
이번 복습에서 가장 많이 헷갈렸던 부분은 Prefix Sum과 Sliding Window의 선택 기준이었다.
두 알고리즘 모두 구간 합 계산에 활용할 수 있지만 적합한 상황이 다르다.
Prefix Sum이란?
Prefix Sum은 배열의 시작 위치부터 각 지점까지의 누적 합을 저장하는 방법이다.
예를 들어 다음 배열이 있다고 가정하자.
numbers = [2, 3, 1, 4]
맨 앞에 0을 추가하는 방식으로 Prefix Sum 배열을 만들면 다음과 같다.
prefix = [0, 2, 5, 6, 10]
이 배열을 사용하면 임의 구간의 합을 다음과 같이 계산할 수 있다.
구간 합 [left, right]
= prefix[right + 1] - prefix[left]
예를 들어 인덱스 1부터 3까지의 합은 다음과 같다.
prefix[4] - prefix[1]
= 10 - 2
= 8
Prefix Sum의 시간·공간 복잡도
작업복잡도
| 전처리 | O(N) |
| 구간 합 조회 | O(1) |
| 추가 공간 | O(N) |
데이터가 변경되지 않고 서로 다른 구간의 합을 여러 번 조회해야 한다면 적합하다.
Sliding Window와의 차이
Sliding Window는 연속된 구간을 이동하면서 기존 계산 결과를 재활용한다.
고정 길이 구간 합에서는 왼쪽에서 빠지는 값을 빼고 오른쪽에서 새로 들어오는 값을 더하면 된다.
예를 들어 연속된 100개 요소의 합 중 최댓값을 한 번 구한다면 고정 길이 Sliding Window를 사용할 수 있다.
구분Prefix Sum고정 길이 Sliding Window
| 적합한 상황 | 임의 구간 합 반복 조회 | 고정 길이 구간 계산 |
| 전체 처리 시간 | 전처리 O(N) | O(N) |
| 추가 공간 | O(N) | O(1) |
| 핵심 원리 | 누적 합 저장 | 기존 구간 합 갱신 |
처음에는 이 두 알고리즘의 적용 상황을 반대로 선택했다.
하지만 다시 생각해 보니 차이가 명확해졌다.
Prefix Sum은 미리 계산하여 반복 조회를 빠르게 하고, Sliding Window는 구간을 이동하며 계산하는 방식이다.
RangeSum C# 구현에서 실수한 점
누적 합 배열을 이용해 구간 합을 조회하는 메서드를 작성하면서 다음 두 가지 실수가 있었다.
첫 번째는 C# 배열의 길이를 prefix.length라고 작성한 것이다.
C#에서는 Length의 첫 글자가 대문자여야 한다.
두 번째는 오른쪽 인덱스의 범위 조건을 잘못 계산한 것이다.
원본 배열의 길이가 N이라면, 맨 앞에 0을 추가한 Prefix Sum 배열의 길이는 N+1이다.
따라서 원본 배열의 마지막 유효 인덱스는 prefix.Length - 2다.
다음은 이러한 부분을 보완한 학습용 코드다.
static long RangeSum(
long[] prefix,
int left,
int right)
{
ArgumentNullException.ThrowIfNull(prefix);
if (left < 0 || left >= prefix.Length - 1)
throw new ArgumentOutOfRangeException(nameof(left));
if (right < left || right >= prefix.Length - 1)
throw new ArgumentOutOfRangeException(nameof(right));
return prefix[right + 1] - prefix[left];
}
이 코드는 첫 요소가 0이고 길이가 N+1인 정상적인 누적 합 배열을 전달받는다는 전제다.
원본 배열이 비어 있다면 조회할 수 있는 구간이 없으므로 예외를 발생시킨다.
이번 복습에서는 구간 합 계산식과 경계 조건의 논리는 이해했지만, C#의 정확한 문법을 반복해서 틀렸다.
따라서 다음에는 전체 메서드를 다시 작성하고 컴파일까지 확인할 필요가 있다.
3. 디자인 패턴 — Observer와 Decorator의 적용 판단
디자인 패턴은 단순히 코드를 복잡하게 만드는 기술이 아니라, 반복되는 설계 문제를 해결하기 위한 방법이다.
오늘
'서버 공부 > 매일학습' 카테고리의 다른 글
| Day 9 — 오늘 배운 게임 서버 개발 지식 5가지 (0) | 2026.11.11 |
|---|---|
| Day 8 — 오늘 배운 게임 서버 개발 지식 5가지 (0) | 2026.11.10 |
| Day 7 — 오늘 배운 게임 서버 개발 지식 5가지 (0) | 2026.11.09 |
| Day 6 — 오늘 배운 게임 서버 개발 지식 5가지 (0) | 2026.11.07 |
| Day 5 — 오늘 배운 게임 서버 개발 지식 5가지 (0) | 2026.11.06 |
