Day 2 — 오늘 배운 게임 서버 개발 지식 5가지

학습일: 2026년 11월 2일
주제: 자료구조, 알고리즘, 디자인 패턴, C# 비동기, 데이터베이스 트랜잭션

오늘은 기존에 학습했던 개념을 복습하면서 단순히 사용법을 아는 것과 내부 동작 원리를 이해하는 것이 다르다는 점을 확인했다.

특히 C#의 비동기 실행 흐름과 PostgreSQL의 동시성 처리에서 헷갈렸던 부분을 다시 정리했다.

1. 자료구조 — List<T>와 Dictionary의 내부 구조

List<T>는 내부 배열을 관리하는 객체다

C#의 List<T>는 내부적으로 배열을 사용한다.

데이터를 계속 추가하다가 내부 저장 공간이 부족해지면 더 큰 배열을 새로 할당하고 기존 요소들을 복사한다.

이때 List<T> 객체 자체가 새로 만들어지는 것은 아니다. 기존 객체가 유지되면서 내부에서 참조하는 배열이 교체된다.

기존 배열은 더 이상 참조되지 않는다면 GC의 수거 대상이 된다.

또한 중간에 요소를 삽입하거나 삭제하면 다른 요소들의 위치를 이동시켜야 하므로 O(n)의 시간 복잡도가 발생한다.

Dictionary는 해시와 엔트리 배열을 활용한다

Dictionary<TKey, TValue>는 해시값을 이용해 데이터를 탐색한다.

일반적인 .NET 구현에는 버킷 배열과 엔트리 배열이 존재한다.

  • 버킷 배열: 해당 버킷에 연결된 첫 엔트리의 위치를 찾는 역할
  • 엔트리 배열: 해시값, Key, Value, 다음 엔트리 위치 등의 정보 저장

서로 다른 키가 같은 버킷에 배정되는 해시 충돌이 발생할 수도 있다.

이때 next 정보를 통해 같은 버킷에 연결된 다른 엔트리를 탐색하고, 실제 키를 비교해 데이터를 구분한다.

2. 알고리즘 — 다익스트라와 A*의 차이

다익스트라 알고리즘은 시작점에서 현재 노드까지의 누적 이동 비용이 가장 작은 노드를 우선 탐색한다.

반면 A* 알고리즘은 누적 이동 비용에 목적지까지의 추정 비용을 더해 탐색 우선순위를 결정한다.

  • g(n): 시작점부터 현재 노드까지의 누적 비용
  • h(n): 현재 노드에서 목적지까지의 추정 비용
  • f(n) = g(n) + h(n): A*에서 사용하는 평가 비용

예를 들어 두 탐색 후보가 다음과 같다고 가정하자.

노드g(n)h(n)f(n)

A 4 7 11
B 6 2 8

다익스트라는 A를 우선 탐색하지만, A*는 B를 우선 탐색한다.

여기서 중요한 것은 휴리스틱의 정확성이다.

h(n)이 실제 남은 최단 비용을 과대평가하면 최적 경로에 포함된 노드의 탐색 우선순위가 낮아질 수 있다. 따라서 일반적인 A* 최단 경로 보장 조건을 만족하지 못할 수 있다.

단, 과대평가했다고 항상 잘못된 경로를 찾는 것은 아니다.

휴리스틱의 허용성과 일관성 조건은 앞으로 더 복습해야 할 내용이다.

3. 디자인 패턴 — Strategy 패턴

Strategy 패턴은 공통 인터페이스를 통해 서로 다른 알고리즘이나 동작을 교체하여 실행할 수 있도록 만드는 패턴이다.

예를 들어 게임 서버에서 플랫폼마다 결제 검증 방식이 다르다고 가정하자.

Google Play, App Store, 외부 웹상점은 서로 다른 검증 로직을 가질 수 있다.

이때 각 결제 로직이 공통 인터페이스를 구현하도록 설계하면, 결제를 요청하는 코드는 구체적인 플랫폼별 구현에 강하게 의존하지 않아도 된다.

public interface IPaymentStrategy
{
    void Pay();
}

핵심은 인터페이스가 여러 결제 로직을 하나로 합치는 것이 아니라 공통 메서드 계약을 제공한다는 점이다.

인터페이스 타입 변수에 구체적인 구현 객체를 할당하면 실제 호출 시 해당 객체의 메서드가 실행된다. 이를 다형성이라고 한다.

다음 학습에서는 객체의 생성 책임을 다루는 Factory Method 패턴과 비교할 예정이다.

4. C# 게임 서버 — async, Task.Run과 ThreadPool Starvation

async 키워드를 사용했다고 해서 메서드가 자동으로 별도 스레드에서 실행되는 것은 아니다.

async 메서드는 호출한 스레드에서 실행을 시작하며, 미완료된 비동기 작업을 await할 때 실행을 중단하고 호출자에게 제어를 돌려줄 수 있다.

여기서 Thread.Sleep과 Task.Delay는 중요한 차이가 있다.

구분Thread.Sleepawait Task.Delay

동작 현재 스레드 차단 비동기 지연 대기
대기 중 스레드 점유 점유함 지연 완료까지 스레드 점유 불필요
서버에서 고려할 점 스레드 부족 위험 단순 시간 대기에 적합

Task.Run은 작업을 ThreadPool에 예약한다.

하지만 Task.Run 내부에서 Thread.Sleep을 실행하면 해당 작업을 실행하는 ThreadPool 스레드가 차단된다.

이러한 작업이 많아지면 ThreadPool에서 처리 가능한 작업 수가 줄어들고, 다른 요청들이 대기하면서 응답 지연이 발생할 수 있다.

이것이 ThreadPool Starvation과 관련된 중요한 문제다.

따라서 I/O 대기나 단순 지연에 불필요하게 Task.Run과 차단 메서드를 사용하는 것은 피해야 한다.

5. 게임 서버 DB — MVCC, SELECT FOR UPDATE와 조건부 UPDATE

PostgreSQL의 READ COMMITTED 격리 수준에서는 일반 SELECT가 다른 트랜잭션의 미커밋 변경 결과를 읽지 않는다.

예를 들어 기존 골드가 1,000인데 트랜잭션 A가 500으로 수정하고 아직 커밋하지 않았다면, 다른 트랜잭션의 일반 SELECT는 기존에 커밋된 1,000을 읽을 수 있다.

이는 MVCC를 이용해 적절한 데이터 버전을 조회하기 때문이다.

반면 SELECT FOR UPDATE는 대상 행의 잠금을 획득하려고 하므로 충돌하는 행 잠금이 있다면 대기해야 한다.

게임 서버에서 조건부 UPDATE 활용하기

플레이어에게 골드가 100 있고, 80골드짜리 아이템을 동시에 두 번 구매한다고 가정해 보자.

UPDATE players
SET gold = gold - 80
WHERE id = 1
  AND gold >= 80;

PostgreSQL의 READ COMMITTED 환경에서 첫 번째 트랜잭션이 차감을 수행하고 잠금을 유지하고 있다면 두 번째 UPDATE는 대기한다.

첫 번째 트랜잭션이 커밋한 뒤에는 변경된 골드를 기준으로 WHERE 조건을 다시 평가한다.

골드가 20이면 gold >= 80이 성립하지 않으므로 두 번째 차감은 수행되지 않는다.

이처럼 조건부 UPDATE와 영향받은 행 수 검사를 이용하면 특정 단일 행의 잔액 부족 문제를 안전하게 처리할 수 있다.

단, 실제 구매 시스템에서는 아이템 지급, 트랜잭션 원자성, 중복 요청 방지까지 별도로 고려해야 한다.


오늘 새롭게 알게 된 점

  1. List<T>의 내부 배열이 확장되어도 List 객체 자체는 유지된다.
  2. Dictionary의 버킷 배열과 엔트리 배열은 서로 다른 역할을 한다.
  3. A*의 휴리스틱은 단순히 추정 비용을 추가하는 것이 아니라 최단 경로 보장에도 영향을 준다.
  4. async는 별도 스레드 실행을 의미하지 않으며, Task.Run 역시 스레드 부족을 해결하는 방법은 아니다.
  5. PostgreSQL에서는 행 잠금이 있어도 일반 SELECT가 MVCC를 이용해 이전에 커밋된 데이터를 읽을 수 있다.

추가로 복습할 내용

  • Dictionary의 해시 충돌과 next 연결 구조
  • A*의 허용성 및 일관성 조건
  • Factory Method와 Strategy 패턴의 차이
  • ThreadPool Starvation의 실제 발생 조건과 진단
  • PostgreSQL의 MVCC, 행 잠금 및 데드락 처리

이번 학습을 통해 단순히 결과를 기억하는 것보다 왜 그런 결과가 발생하는지 설명할 수 있어야 한다는 점을 다시 확인했다.

앞으로도 게임 서버의 데이터 정합성, 동시성, 성능 문제를 내부 동작 원리와 연결해 학습할 예정이다.

예시 코드는 학습 목적으로 작성했으며, 실제 실행 및 부하 테스트를 수행한 결과는 포함하지 않았다.

C# 웹 서버 프로젝트를 생성하고 실행하는 과정까지 정리

로컬 컴퓨터에서 HTTP 서버를 실행하고 웹 브라우저로 접속했을 때 Hello World!가 출력되는 것을 확인

1. .NET이란?

.NET은 Microsoft에서 개발한 소프트웨어 개발 플랫폼이다.

C# 등의 프로그래밍 언어를 사용하여 애플리케이션을 개발하고 실행할 수 있는 환경을 제공한다.

 

예를 들어 C#으로 게임 서버를 작성했다고 가정해 보자.

작성한 C# 코드가 실제로 실행되기 위해서는 컴파일 과정과 실행 환경이 필요하다.

.NET은 이러한 개발 및 실행에 필요한 도구와 환경을 제공한다.

.NET을 이용하면 웹 서버뿐만 아니라 콘솔 프로그램, 데스크톱 애플리케이션 등 다양한 프로그램을 개발할 수 있다.

참고로 과거에는 .NET Core라는 이름을 사용했지만, .NET 5부터는 공식적으로 .NET이라는 이름을 사용한다.

단, 웹 개발 프레임워크인 ASP.NET Core는 현재도 해당 명칭을 사용한다.

주요 용어

C#

  • 프로그램을 작성하는 프로그래밍 언어

.NET

  • C# 등의 언어로 작성한 프로그램을 개발하고 실행할 수 있도록 지원하는 플랫폼

SDK (Software Development Kit)

  • 소프트웨어 개발 도구 모음
  • 프로젝트 생성, 컴파일, 빌드 등에 필요한 도구 제공

Runtime (런타임)

  • 빌드된 .NET 애플리케이션을 실행하는 데 필요한 환경

ASP.NET Core

  • .NET 기반으로 웹 서버 및 HTTP API를 개발할 수 있는 프레임워크
  • HTTP 요청 처리, 라우팅, 미들웨어 등의 기능 제공

SDK와 런타임의 차이

SDK는 프로그램을 개발하기 위한 도구, 런타임은 프로그램을 실행하기 위한 환경

 

예를 들어 개발자는 SDK를 사용하여 게임 서버를 만들고,

서버를 실행할 컴퓨터에는 해당 애플리케이션과 호환되는 런타임이 필요할 수 있다.

일반적으로 .NET SDK를 설치하면 개발에 필요한 런타임도 함께 설치된다.

단, 애플리케이션을 배포하는 방식에 따라 별도의 런타임 설치가 필요하지 않을 수도 있다.


2. dotnet 명령어

.NET SDK를 설치하면 터미널에서 dotnet 명령어를 사용할 수 있다.

명령어설명

dotnet --version 현재 선택되는 .NET SDK 버전 확인
dotnet --list-sdks 설치된 SDK 목록 확인
dotnet --list-runtimes 설치된 런타임 목록 확인
dotnet new 새 프로젝트 생성
dotnet build 프로젝트 빌드
dotnet run 프로젝트 빌드 및 실행
dotnet test 테스트 실행

설치된 SDK 버전 확인

터미널에서 다음 명령어를 실행한다.

dotnet --version

예시 출력:

10.0.100

현재 선택되는 SDK 버전이 10.0.100이라는 의미다.

여기서 확인하는 것은 C# 언어 버전이나 런타임 버전이 아니라 .NET SDK 버전이다.

설치된 SDK를 모두 확인하려면 다음 명령어를 사용한다.

dotnet --list-sdks

이 명령어를 사용하면 여러 버전의 SDK가 설치되어 있는지도 확인할 수 있다.


3. ASP.NET Core 서버 프로젝트 생성

이번에는 ASP.NET Core를 사용하는 새로운 웹 서버 프로젝트를 생성한다.

사용할 프로젝트 이름은 MergeGame.Lab.Server이다.

dotnet new web -n MergeGame.Lab.Server -o MergeGame.Lab.Server -f net10.0

 

dotnet new

dotnet new

새로운 프로젝트나 파일을 미리 정의된 템플릿을 기반으로 생성하는 명령어

프로젝트를 처음 만들 때 필요한 기본 파일과 설정을 자동으로 생성해 준다.

web

web

ASP.NET Core의 빈 웹 프로젝트 템플릿을 의미한다.

이 템플릿은 웹 서버를 실행하는 데 필요한 최소한의 구성으로 프로젝트를 생성한다.

복잡한 기능이 미리 추가되어 있지 않기 때문에 서버의 구조를 직접 학습하기에 적합하다.

-n

-n MergeGame.Lab.Server

-n은 --name의 줄임말로, 생성할 프로젝트의 이름을 지정한다.

여기서는 프로젝트 이름을 MergeGame.Lab.Server로 설정한다.

주의할 점은 -n이 프로젝트의 저장 폴더를 지정하는 옵션은 아니라는 것

-o

-o MergeGame.Lab.Server

-o는 --output의 줄임말로, 생성된 프로젝트 파일을 저장할 위치를 지정한다.

현재 위치를 기준으로 MergeGame.Lab.Server 폴더에 프로젝트 파일을 생성한다.

-o를 생략하면 기본적으로 현재 디렉터리에 프로젝트 파일이 생성된다.

따라서 프로젝트 이름과 폴더 이름을 각각 지정하고 싶다면 -n과 -o를 함께 사용하면 된다.

-f

-f net10.0

-f는 --framework의 줄임말로, 프로젝트의 대상 프레임워크를 지정한다.

net10.0은 해당 프로젝트가 .NET 10을 대상으로 빌드되도록 설정한다는 의미다.

프로젝트 생성 시 .csproj 파일에 다음과 같은 설정이 포함된다.

<TargetFramework>net10.0</TargetFramework>

주의할 점

-f net10.0은 .NET 10을 설치하는 명령어가 아니다.

또한 특정 SDK 버전을 직접 선택하는 명령어도 아니다.

프로젝트가 대상으로 사용할 .NET 프레임워크 버전을 지정하는 옵션이다.

이 예제를 실행하려면 .NET 10 프로젝트 생성을 지원하는 SDK가 설치되어 있어야 한다.


4. 생성된 프로젝트 구조

명령어 실행 후 다음과 같은 프로젝트 파일이 생성된다.

MergeGame.Lab.Server/
│
├── Program.cs
│
├── MergeGame.Lab.Server.csproj
│
├── appsettings.json
│
├── appsettings.Development.json
│
└── Properties/
    └── launchSettings.json

 

프로젝트 생성 및 빌드 과정에 따라 obj, bin 등의 폴더도 생성된다.

Program.cs

서버 애플리케이션의 시작 코드가 작성되는 파일

서버의 초기 설정, HTTP 요청 처리 경로 등록, 애플리케이션 실행 등의 코드가 포함된다.

MergeGame.Lab.Server.csproj

C# 프로젝트 설정 파일

프로젝트가 대상으로 하는 .NET 버전, 패키지 참조, 빌드 설정 등의 정보가 포함된다.

예를 들어 .NET 10을 사용하는 프로젝트라면 대상 프레임워크가 net10.0으로 지정되어 있다.

appsettings.json

애플리케이션에서 사용하는 기본 설정을 저장하는 파일

예를 들어 로그 설정이나 서버에서 사용하는 사용자 정의 설정 등을 저장할 수 있다.

appsettings.Development.json

개발 환경에서 적용할 설정을 저장하는 파일

ASP.NET Core는 개발 환경과 운영 환경 등에 따라 서로 다른 설정을 적용할 수 있다.

일반적으로 개발 환경에서는 기본 설정인 appsettings.json에 개발용 설정을 추가하거나 덮어써 사용할 수 있다.

launchSettings.json

로컬 개발 시 애플리케이션을 어떻게 시작할지 정의하는 설정 파일

실행 프로필, 개발용 URL, 환경 변수 등을 지정할 수 있다.

주로 로컬 개발 및 디버깅 과정에서 사용된다.


5. 프로젝트 실행

먼저 생성된 프로젝트 폴더로 이동한다.

cd MergeGame.Lab.Server

cd는 현재 작업 디렉터리를 변경하는 명령어다.

이제 다음 명령어를 실행한다.

dotnet run --urls "http://localhost:5080"

이 명령어는 프로젝트를 빌드하고 실행하며, 웹 서버가 지정한 주소에서 HTTP 요청을 받을 수 있도록 설정한다.

dotnet run

dotnet run

현재 프로젝트를 빌드하고 실행하는 명령어다.

일반적으로 필요한 패키지 복원과 빌드를 수행한 뒤 애플리케이션을 실행한다.

따라서 기본적인 개발 과정에서는 dotnet build를 별도로 실행하지 않고도 서버를 실행할 수 있다.

--urls

--urls "http://localhost:5080"

ASP.NET Core 애플리케이션이 요청을 수신할 URL을 지정하는 옵션이다.

여기서는 현재 컴퓨터의 5080번 포트에서 HTTP 요청을 받을 수 있도록 설정한다.

--urls는 ASP.NET Core 애플리케이션에 전달되는 설정 옵션이다.

참고로 --urls 앞의 --는 옵션 이름의 일부다.

이와 별도로 dotnet run에서는 독립적인 --를 사용하여 실행 도구의 인수와 애플리케이션에 전달할 인수를 구분할 수도 있다.

예를 들어 다음과 같이 작성할 수도 있다.

dotnet run -- --urls "http://localhost:5080"

두 명령어 모두 이 예제에서는 같은 서버 주소를 설정할 수 있다.


6. URL 구성 요소 이해하기

이번에 사용한 주소를 분리해서 살펴보자.

http://localhost:5080

 

 

구성 요소의미

http HTTP 통신 프로토콜
localhost 현재 컴퓨터를 가리키는 호스트 이름
5080 서버가 요청을 받을 포트 번호

HTTP란?

HTTP는 클라이언트와 서버가 요청과 응답을 주고받기 위한 통신 규약이다.

예를 들어 클라이언트가 서버에 게임 유저 정보를 요청한다면, 서버는 해당 요청을 처리하고 결과를 HTTP 응답으로 반환할 수 있다.

다만 HTTP 자체는 통신 내용을 암호화하지 않는다.

실제 서비스에서는 일반적으로 TLS 암호화를 적용한 HTTPS를 사용한다.

localhost란?

localhost는 현재 컴퓨터 자신을 가리키는 호스트 이름이다.

일반적인 환경에서는 다음 루프백 주소로 연결된다.

  • IPv4: 127.0.0.1
  • IPv6: ::1

따라서 로컬 환경에서 다음과 같은 주소로 서버에 접근할 수 있다.

http://localhost:5080
http://127.0.0.1:5080

두 주소는 엄밀히 말해 동일한 주소는 아니다.

localhost는 호스트 이름이고 127.0.0.1은 IPv4 주소이기 때문이다.

하지만 서버가 해당 IPv4 주소에서 요청을 받을 수 있도록 설정되어 있다면 둘 다 같은 로컬 서버에 접근할 수 있다.

또한 localhost로 서버를 실행했다고 해서 외부 컴퓨터에서도 접속할 수 있는 것은 아니다.

현재 예제는 자신의 컴퓨터에서 서버를 실행하고 테스트하기 위한 설정이다.

Port란?

포트는 한 컴퓨터에서 네트워크 통신을 담당하는 프로그램이나 연결 지점을 구분할 때 사용하는 번호

예를 들어 하나의 컴퓨터에서 여러 서버 프로그램을 실행한다고 가정해 보자.

127.0.0.1:5080
127.0.0.1:6000
127.0.0.1:7000

각 서버가 서로 다른 포트를 사용한다면, 클라이언트는 목적지 포트에 따라 원하는 서버로 연결할 수 있다.

IP 주소가 네트워크상의 컴퓨터를 구분하는 데 사용된다면,

포트 번호는 해당 컴퓨터에서 통신할 대상을 구분하는 데 사용된다고 이해하면 된다.


7. dotnet run 실행 시 내부 동작 과정

그렇다면 dotnet run을 실행하면 내부적으로 어떤 일이 발생할까?

전체 과정을 간단하게 정리하면 다음과 같다.

1단계. 프로젝트 확인

.NET SDK가 프로젝트의 .csproj 파일과 관련 설정을 확인한다.

어떤 대상 프레임워크를 사용하는지, 어떤 패키지를 참조하는지 등의 정보를 확인한다.

2단계. 빌드

C# 소스코드를 컴파일하여 실행에 필요한 결과물을 생성한다.

일반적인 .NET 빌드에서는 C# 코드가 IL(중간 언어)을 포함한 어셈블리로 컴파일된다.

빌드 결과물은 보통 프로젝트의 bin 폴더 아래에 생성된다.

3단계. 프로세스 실행

.NET이 빌드된 애플리케이션을 실행한다.

운영체제는 실행 중인 서버 애플리케이션을 프로세스로 관리한다.

4단계. 웹 서버 시작

ASP.NET Core에서 기본적으로 사용하는 Kestrel 웹 서버가 시작된다.

Kestrel은 설정된 네트워크 주소에서 연결을 수신하고 HTTP 요청을 처리하는 웹 서버다.

5단계. 요청 대기

Kestrel이 설정된 주소와 포트에서 요청을 받을 준비를 마친다.

이번 예제에서는 다음 주소를 사용한다.

http://localhost:5080

이후 클라이언트가 HTTP 요청을 보내면 서버는 등록된 요청 처리 코드에 따라 응답을 반환한다.


8. 주요 개념 정리: 컴파일, 프로세스, Kestrel

컴파일(Compile)

사람이 작성한 소스코드를 컴퓨터가 실행할 수 있는 형태로 변환하는 과정이다.

일반적인 .NET 실행 과정은 다음과 같다.

C# 소스코드
      ↓
C# 컴파일러
      ↓
IL(중간 언어)
      ↓
.NET 런타임
      ↓
JIT 컴파일
      ↓
기계어 실행

C# 코드는 먼저 IL로 컴파일된다.

그리고 일반적인 JIT 실행 방식에서는 런타임이 실행에 필요한 코드를 기계어로 변환한다.

다만 .NET에는 미리 네이티브 코드로 컴파일하는 AOT 등의 다른 실행 방식도 존재한다.

프로세스(Process)

프로세스는 운영체제에서 실행 중인 프로그램의 인스턴스

예를 들어 서버 프로그램을 실행하면 운영체제는 해당 프로그램을 실행 중인 프로세스로 관리한다.

같은 프로그램을 여러 번 실행하면 여러 프로세스가 생성될 수도 있다.

프로세스는 자신이 사용하는 메모리와 운영체제 자원 등을 가진다.

Kestrel

Kestrel은 ASP.NET Core에서 기본적으로 사용하는 웹 서버

주요 역할은 다음과 같다.

  • 네트워크 연결 수신
  • HTTP 요청 수신 및 처리
  • ASP.NET Core 애플리케이션으로 요청 전달
  • 애플리케이션의 HTTP 응답을 클라이언트로 전송

여기서 중요한 점은 Kestrel이 HTTP 통신을 처리하는 웹 서버이고,

실제 게임 로직은 개발자가 작성한 애플리케이션 코드에서 수행할 수 있다는 것이다.

 

예를 들어 게임 서버를 개발한다면 로그인, 재화 처리, 아이템 생성, 머지 로직 등은 애플리케이션에 별도로 구현해야 한다.

Kestrel은 이러한 기능 자체를 구현해 주는 것이 아니라 클라이언트가 HTTP를 통해 애플리케이션과 통신할 수 있도록 지원한다.


9. Program.cs 코드 살펴보기

이번에 생성한 빈 ASP.NET Core 프로젝트의 Program.cs를 열어보면 다음과 같은 코드가 작성되어 있다.

var builder = WebApplication.CreateBuilder(args);

var app = builder.Build();

app.MapGet("/", () => "Hello World!");

app.Run();

각 코드를 살펴보자.

WebApplication.CreateBuilder(args)

var builder = WebApplication.CreateBuilder(args);

ASP.NET Core 애플리케이션을 구성하기 위한 빌더를 생성한다.

서버 설정, 환경 변수, 명령줄 인수, 의존성 주입 등에 필요한 기본 구성을 준비한다.

여기서 args는 프로그램을 실행할 때 전달받은 명령줄 인수를 의미한다.

앞서 사용한 --urls와 같은 설정도 애플리케이션의 구성에 반영될 수 있다.

builder.Build()

var app = builder.Build();

빌더에 설정된 내용을 기반으로 WebApplication 객체를 생성한다.

이 객체를 통해 HTTP 요청 처리 경로를 등록하고 서버를 실행할 수 있다.

app.MapGet()

app.MapGet("/", () => "Hello World!");

HTTP GET 요청을 처리할 경로를 등록한다.

각 부분의 의미는 다음과 같다.

  • MapGet: GET 방식의 HTTP 요청을 처리할 경로 등록
  • /: 요청을 받을 URL의 루트 경로
  • () => "Hello World!": 요청을 받았을 때 실행할 람다식

즉 클라이언트가 / 경로로 GET 요청을 보내면 Hello World! 문자열을 응답으로 반환하도록 설정한 것이다.

app.Run()

app.Run();

구성된 ASP.NET Core 애플리케이션을 시작한다.

이 과정에서 웹 서버가 시작되고, 설정된 주소에서 HTTP 요청을 받을 수 있게 된다.

일반적인 서버 프로그램에서는 종료 요청을 받기 전까지 실행 상태를 유지한다.


10. 서버 실행 및 접속 테스트

서버를 실행

dotnet run --urls "http://localhost:5080"

정상적으로 실행되면 콘솔에 다음과 비슷한 로그가 출력된다.

info: Microsoft.Hosting.Lifetime[14]
      Now listening on: http://localhost:5080

info: Microsoft.Hosting.Lifetime[0]
      Application started. Press Ctrl+C to shut down.

실제 로그의 세부 내용은 실행 환경에 따라 달라질 수 있다.

여기서 중요한 부분은 다음 문장이다.

Now listening on: http://localhost:5080

서버가 해당 주소에서 요청을 받을 준비를 마쳤다는 의미

이제 크롬과 같은 웹 브라우저를 열고 다음 주소로 접속한다.

http://localhost:5080/

정상적으로 실행되었다면 화면에 다음 문자열이 나타난다.

Hello World!

이 문자열이 출력되는 이유는 Program.cs에 다음 코드가 등록되어 있기 때문이다.

app.MapGet("/", () => "Hello World!");

11. 브라우저에서 접속할 때 실제 동작

브라우저에 http://localhost:5080/을 입력했을 때의 전체 흐름

[웹 브라우저]
      │
      │ HTTP GET /
      ▼
[localhost:5080]
      │
      ▼
[Kestrel 웹 서버]
      │
      ▼
[ASP.NET Core 애플리케이션]
      │
      ▼
[MapGet("/")에 등록된 코드]
      │
      │ "Hello World!" 반환
      ▼
[HTTP 응답]
      │
      ▼
[웹 브라우저에 출력]
  1. 브라우저가 서버에 HTTP GET 요청을 보낸다.
  2. Kestrel이 해당 요청을 수신한다.
  3. ASP.NET Core 애플리케이션이 요청을 처리할 경로를 찾는다.
  4. / 경로에 등록된 코드가 실행된다.
  5. Hello World! 문자열이 HTTP 응답으로 반환된다.
  6. 브라우저가 응답받은 문자열을 화면에 표시한다.

이렇게 간단한 예제를 통해 클라이언트와 서버가 요청과 응답을 주고받는 기본 흐름을 확인할 수 있다.


12. 서버 종료

실행 중인 서버를 종료하려면 서버가 실행되고 있는 터미널에서 다음 단축키를 누른다.

Ctrl + C

그러면 애플리케이션에 종료 신호가 전달되고 서버가 종료 절차를 진행한다.

서버가 종료되면 더 이상 해당 서버에서 HTTP 요청을 처리할 수 없다.


13. 최종 정리

 .NET과 ASP.NET Core의 기본 개념부터 웹 서버를 생성하고 실행하는 과정

 

사용한 주요 명령어는 다음과 같다.

1. SDK 버전 확인

dotnet --version

2. 프로젝트 생성

dotnet new web -n MergeGame.Lab.Server -o MergeGame.Lab.Server -f net10.0

3. 프로젝트 폴더 이동

cd MergeGame.Lab.Server

4. 서버 실행

dotnet run --urls "http://localhost:5080"

5. 브라우저 접속

http://localhost:5080/

6. 서버 종료

Ctrl + C

참고 자료

 

릴레이션의 키

- 각 투플을 고유하게 식별할 수 있는 하나 이상의 애트리뷰트들의 모임

 

키의 유형 정의
수퍼 키 한 릴레이션 내의 특정 투플을 고유하게 식별하는 하나의 애트리뷰트 또는 애트리뷰트의 집합
후보 키 각 투플을 고유하게 식별하는 최소한의 애트리뷰트들의 모임
기본 키 후보 키가 두 개 이 상 있으면 설계자 또는 데이터베이스 관리자가 이들 중에서 하나를 기본키로 선정
대리 키 인위적으로 추가된 기본 키
대체 키 기본 키로 선정되지 않은 후보 키
외래 키 어떤 릴레이션의 기본 키를 참조하는 애트리뷰트

릴레이션 : 2차원의 테이블

레코드, 투플 : 릴레이션의 각 행

애트리뷰트 : 릴레이션에서 이름을 가진 하나의 열

 

 

'서버 공부 > DB 공부' 카테고리의 다른 글

INNER JOIN vs OUTER JOIN 차이점  (0) 2022.03.01
Chapter 01 데이터베이스 시스템  (0) 2018.01.02

+ Recent posts