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

https://programmers.co.kr/learn/courses/30/lessons/1845

 

코딩테스트 연습 - 폰켓몬

당신은 폰켓몬을 잡기 위한 오랜 여행 끝에, 홍 박사님의 연구실에 도착했습니다. 홍 박사님은 당신에게 자신의 연구실에 있는 총 N 마리의 폰켓몬 중에서 N/2마리를 가져가도 좋다고 했습니다.

programmers.co.kr

문제 접근 풀이

1. 폰켓몬 종류의 최대값을 return하면 되니까 같은 값의 경우 무시하고 다른 값들에 대해서 접근한다

2. 정렬을 해놓은 상태로 접근하면 한번의 for문으로 중복된 값을 신경쓰지 않고 처리할 수 있다고 생각했다

3. 정렬을 하고 같은 값인 경우 넘어가고 아닌 경우의 값을 저장하여 비교한다 answer의 값은 1 증가시켜준다

이상태로 제출하니 맞고 틀리고 하는 상황이 발생했는데

폰켓몬을 선택할 수 있는 것은 총 수의 n/2 이므로 마지막에 조건을 추가해주어 정답에 도출하게 되었다

 

총코드

#include <vector>
#include <algorithm>
using namespace std;

int solution(vector<int> nums)
{
    int answer = 1;
    sort(nums.begin(),nums.end());
    int tempNum = nums[0];
    for(auto i : nums)
    {
       if(tempNum == i)
           continue;
        else
        {
            tempNum = i;
            answer++;
        }
    }
    if(answer > nums.size()/2)
        return nums.size()/2;
    return answer;
}

+ Recent posts