C# Coding Style Rule
.NET Runtime 공식 가이드 기반 실무 정리
Microsoft .NET Runtime의 C# Coding Style과 Microsoft Learn의 .NET 코드 스타일 문서를 기준으로, 실제 ASP.NET Core 개발에서 적용할 수 있는 핵심 규칙과 자동화 방법을 정리합니다.
공식 문서의 현재 내용을 확인해 작성했습니다. .NET Runtime 저장소 규칙과 Microsoft Learn의 일반적인 .NET 코드 스타일 규칙은 서로 관련되어 있지만 동일한 문서가 아니므로 구분해서 설명합니다.
1. 먼저 구분해야 할 것: Runtime 규칙과 일반 .NET 규칙
이번 글의 가장 중요한 전제는 .NET Runtime 저장소의 Coding Style을 모든 C# 프로젝트의 의무 규칙으로 오해하지 않는 것입니다.
Microsoft의 .NET 문서도 코딩 규칙은 프로젝트의 일관성과 유지보수를 위한 기준이며, 각 프로젝트에서 필요에 따라 수정해서 사용할 수 있다고 설명합니다. .NET Runtime과 Microsoft의 문서 저장소는 상당 부분 같은 계열의 규칙을 채택하지만 저장소별 규칙과 .editorconfig가 실제 적용 기준이 됩니다.
새 프로젝트라면 팀 규칙을 먼저 정하고 .editorconfig로 자동화합니다. 기존 프로젝트라면 전체 파일을 한 번에 재포맷하기보다 해당 저장소의 기존 코드와 .editorconfig를 먼저 확인합니다.
2. 핵심 규칙 한눈에 보기
일반적인 Microsoft C# 문서 규칙은 공백 4칸을 사용하고 탭 문자를 사용하지 않는 형태를 제시합니다.
Allman 스타일처럼 여는 중괄호를 다음 줄에 배치하는 형식을 사용합니다.
private/internal 비상수 인스턴스 필드는 _camelCase 형태를 사용합니다.
Microsoft 문서의 관련 규칙에서는 static 필드에 s_ 접두사를 사용하는 형태를 설명합니다.
메서드와 타입 이름은 일반적으로 PascalCase를 사용합니다.
private/internal을 포함한 상수 이름도 PascalCase를 사용합니다.
3. 중괄호와 들여쓰기
3-1. Allman 스타일
Microsoft의 C# 코딩 규칙 문서에서는 여는 중괄호와 닫는 중괄호를 별도의 줄에 배치하는 Allman 스타일을 사용합니다.
if (condition)
{
DoSomething();
}
else
{
DoSomethingElse();
}
3-2. 공백 4칸
Microsoft의 .NET Coding Conventions는 들여쓰기에 공백 4칸을 사용하고 탭 문자를 사용하지 않는 형식을 설명합니다.
public class UserService
{
public void Execute()
{
var user = GetUser();
if (user != null)
{
Process(user);
}
}
}
4. 이름을 읽으면 역할이 보이게 만들기
Microsoft 문서에서 제시하는 대표적인 명명 규칙은 타입과 메서드에는 PascalCase, 매개변수와 지역 변수에는 camelCase를 사용하는 것입니다. private/internal 비상수 인스턴스 필드는 앞에 밑줄을 붙인 형태를 사용합니다.
public sealed class UserService
{
private readonly IUserRepository _userRepository;
public UserService(IUserRepository userRepository)
{
_userRepository = userRepository;
}
public User GetUser(int userId)
{
return _userRepository.Get(userId);
}
}
private 필드의 _는 단순 장식이 아니라 매개변수와 필드를 시각적으로 구분하는 데 도움을 줍니다. 다만 최종 적용 여부는 프로젝트의 .editorconfig가 결정합니다.
5. static 필드와 상수
Microsoft의 identifier naming 문서에서는 static 필드에 s_ 접두사를 사용하는 예를 설명합니다. 또한 상수는 접근 제한자와 관계없이 PascalCase를 사용하는 형태를 제시합니다.
private static readonly object s_lock = new();
private const int MaxRetryCount = 3;
public const string DefaultRole = "User";
참고: s_는 Visual Studio의 기본 동작이나 Framework Design Guidelines 전체의 강제 규칙이 아니라 .editorconfig로 구성할 수 있는 명명 convention입니다.
6. 접근 제한자와 modifier 순서
코드에서 접근 수준을 명확하게 표현하면 타입과 멤버의 API 경계를 읽기 쉬워집니다. Runtime 계열의 코드 스타일에서는 visibility modifier를 다른 modifier보다 먼저 배치하는 형태를 사용합니다.
public sealed class UserService
{
private readonly IUserRepository _repository;
public UserService(IUserRepository repository)
{
_repository = repository;
}
private void Validate()
{
}
}
7. this.는 프로젝트 규칙에 따라 결정
여기서 원문을 조금 수정할 필요가 있습니다. this.를 항상 생략해야 한다고 일반화하기보다, .NET 코드 스타일의 qualification 옵션으로 프로젝트가 선택할 수 있다고 보는 것이 정확합니다.
private string _name;
public User(string name)
{
_name = name;
}
따라서 AI에게 무조건 this.를 붙이거나 제거하도록 지시하기보다 .editorconfig의 qualification 규칙을 프로젝트 기준으로 확인하는 편이 안전합니다.
8. using 정렬은 자동화하자
Microsoft의 .NET formatting options에는 dotnet_sort_system_directives_first와 dotnet_separate_import_directive_groups 같은 using 정렬·그룹화 옵션이 있습니다.
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Extensions.Logging;
중요한 것은 글에서 정한 순서보다 현재 저장소의 .editorconfig 설정입니다. IDE가 자동 정렬하도록 만들면 개발자마다 다른 결과가 나오는 문제를 줄일 수 있습니다.
9. var는 금지 규칙이 아니다
var 사용 여부는 .NET code-style 규칙으로 구성할 수 있습니다. 따라서 “var는 반드시 사용” 또는 “var는 절대 사용 금지”라고 단정하기보다 프로젝트의 규칙을 확인해야 합니다.
var stream = new FileStream(path, FileMode.Open);
Stream stream = GetStream();
첫 번째 코드는 오른쪽 생성자를 통해 타입이 즉시 드러나는 반면 두 번째 코드는 반환 메서드의 선언을 확인해야 타입을 알 수 있습니다. 실제 저장소에서는 .editorconfig와 기존 코드 스타일을 함께 확인해야 합니다.
10. target-typed new()
target-typed new()는 왼쪽의 선언 타입을 통해 생성할 타입을 판단합니다. 가독성은 왼쪽 선언부에서 충분한 정보를 제공하는지와 함께 판단해야 합니다.
private readonly List<string> _items = new();
var users = new List<User>();
11. C# 키워드 타입과 System 타입명
Microsoft의 C# 문서와 Runtime 계열 코드에서는 기본 타입을 표현할 때 C# 키워드인 int, string 등을 사용하는 convention을 확인할 수 있습니다.
| 일반적인 C# 표현 | 동일 CLR 타입 표현 |
|---|---|
int | System.Int32 |
string | System.String |
bool | System.Boolean |
12. nameof()으로 문자열 기반 이름을 줄이기
변수나 멤버의 이름을 문자열로 직접 작성해야 하는 경우 nameof()를 사용하면 리팩터링 시 이름과 문자열이 어긋나는 문제를 줄일 수 있습니다.
throw new ArgumentNullException("user");
throw new ArgumentNullException(nameof(user));
두 코드는 예외 메시지에 사용되는 매개변수 이름을 표현하는 방식이 다릅니다. nameof(user)는 컴파일러가 식별자 이름을 기반으로 문자열을 생성하므로 이름 변경 시 함께 추적할 수 있습니다.
13. 메서드와 local function 명명
메서드는 PascalCase를 사용합니다. Microsoft 문서의 식별자 규칙에서는 메서드 이름과 타입 이름을 PascalCase로 설명하며, local function 역시 프로젝트의 명명 규칙에 맞춰 일관되게 관리해야 합니다.
private User GetUser()
{
return LoadUser();
}
private void ProcessUser(User user)
{
}
public void Execute()
{
void ValidateUser()
{
}
ValidateUser();
}
14. 클래스 내부의 선언 순서는 저장소 규칙을 확인
필드 → 생성자 → 메서드와 같은 순서는 실무에서 흔히 사용하는 구조이지만, 모든 C# 프로젝트에 하나의 순서가 강제되는 것은 아닙니다. 기존 저장소의 규칙과 .editorconfig, IDE 정리 설정을 먼저 확인해야 합니다.
public sealed class UserService
{
private readonly IUserRepository _repository;
private readonly ILogger<UserService> _logger;
public UserService(
IUserRepository repository,
ILogger<UserService> logger)
{
_repository = repository;
_logger = logger;
}
public User GetUser(int userId)
{
return _repository.Get(userId);
}
}
15. if 문은 명확하게 작성
중괄호 사용 여부는 프로젝트 formatting rule에 의해 결정할 수 있습니다. 팀에서 일관성을 중요하게 본다면 단일 문장이라도 중괄호를 사용하는 규칙을 선택할 수 있습니다.
if (condition)
{
throw new InvalidOperationException();
}
16. sealed와 static은 설계 의도를 표현하는 도구
상속이 필요하지 않은 구현 타입에 sealed를 사용하는 것은 타입의 설계 의도를 표현하는 방법입니다. 다만 “모든 private/internal 타입은 반드시 sealed 또는 static”이라고 일반화하기보다 실제 상속 가능성, 테스트 전략, 공개 API 설계를 함께 고려해야 합니다.
private sealed class UserValidator
{
}
17. Primary Constructor에서는 parameter와 field를 구분
Primary constructor parameter는 일반적으로 camelCase 이름을 사용합니다. private field로 저장해야 한다면 기존 field convention에 따라 별도의 필드에 할당할 수 있습니다.
public sealed class UserService(IUserRepository repository)
{
private readonly IUserRepository _repository = repository;
public User GetUser(int userId)
{
return _repository.Get(userId);
}
}
18. Unicode 규칙은 저장소 특수 규칙과 일반 규칙을 구분
원문에 있던 “비 ASCII 문자는 항상 Unicode escape sequence를 사용한다”는 식의 설명은 일반 C# 프로젝트 규칙처럼 읽힐 수 있으므로 주의해야 합니다. 특정 저장소의 문자 처리 규칙은 해당 저장소의 문서와 .editorconfig를 직접 확인해야 합니다.
이 글에서는 일반 C# 프로젝트에 일괄 적용되는 규칙처럼 단정하지 않습니다. 실제 저장소에서 요구하는 경우에만 해당 저장소의 convention을 적용합니다.
19. .editorconfig가 실제 적용 기준이다
Microsoft Learn은 .editorconfig를 솔루션이나 프로젝트에 추가하여 팀 전체의 코드 스타일을 일관되게 유지할 수 있다고 설명합니다. 파일은 디렉터리 계층에 따라 적용되며, Visual Studio 등의 편집기에서 프로젝트별 설정을 적용할 수 있습니다.
root = true
[*.cs]
indent_style = space
indent_size = 4
tab_width = 4
Coding Guideline → .editorconfig → IDE 자동 적용 → dotnet format → CI 검증
20. dotnet format으로 자동 검증
현재 Microsoft Learn의 dotnet format 문서에 따르면 이 명령은 .editorconfig 설정을 기준으로 프로젝트 또는 솔루션의 코드를 포맷합니다. --verify-no-changes 옵션을 사용하면 포맷 변경이 필요한지 검증하고, 변경이 필요하면 0이 아닌 종료 코드를 반환합니다.
dotnet format
dotnet format --verify-no-changes
개발자가 로컬에서 코드를 작성한 뒤 CI에서 동일한 규칙을 다시 검사하면 IDE별 개인 설정 차이로 인한 포맷 불일치를 줄일 수 있습니다.
21. ASP.NET Core 프로젝트에 적용하는 현실적인 방법
| 단계 | 적용 방법 |
|---|---|
| 1. 기준 정의 | 팀 Coding Guideline을 문서화 |
| 2. 자동화 | .editorconfig에 formatting/naming 규칙 설정 |
| 3. 개발 | IDE가 저장·정리 과정에서 규칙 적용 |
| 4. 검증 | dotnet format --verify-no-changes 실행 |
| 5. CI | Pull Request 단계에서 동일 명령 실행 |
22. AI 코딩 시대에는 규칙을 “프롬프트”보다 “검증 가능한 설정”으로 만들기
Cursor, Claude Code, GitHub Copilot 같은 AI 코딩 도구를 사용할 때도 “C# 스타일을 지켜라”라는 자연어 지시만으로 저장소 전체의 일관성을 보장하기는 어렵습니다.
이 구조의 장점은 AI가 생성한 코드도 결국 저장소의 자동화된 규칙과 동일한 검증 절차를 거치게 할 수 있다는 점입니다.
23. CASE STUDY — ASP.NET Core 서비스 코드
다음 코드는 특정 Microsoft 저장소의 복사본이 아니라, 앞서 설명한 원칙을 ASP.NET Core 서비스 코드에 적용한 실무 적용 예시입니다.
using System;
using Microsoft.Extensions.Logging;
public sealed class UserService
{
private readonly IUserRepository _repository;
private readonly ILogger<UserService> _logger;
public UserService(
IUserRepository repository,
ILogger<UserService> logger)
{
_repository = repository;
_logger = logger;
}
public User GetUser(int userId)
{
if (userId <= 0)
{
throw new ArgumentOutOfRangeException(nameof(userId));
}
return _repository.Get(userId);
}
}
- 타입과 메서드는 PascalCase
- private 인스턴스 필드는
_camelCase - 중괄호는 Allman 스타일
- 들여쓰기는 공백 4칸
- 기본 타입은
int - 매개변수 이름에는
nameof()활용
24. 바로 적용하는 Checklist
25. FAQ
Q. .NET Runtime 코딩 규칙을 모든 C# 프로젝트에서 그대로 사용해야 하나요?
아닙니다. Runtime 저장소의 규칙은 해당 저장소의 개발 기준입니다. 다른 프로젝트에서는 팀의 convention과 .editorconfig가 실제 적용 기준이 됩니다.
Q. var는 사용하면 안 되나요?
그렇게 단정할 수 없습니다. .NET code-style 규칙에서 var 관련 선호를 구성할 수 있으므로 프로젝트 설정을 확인해야 합니다.
Q. private 필드는 왜 _를 붙이나요?
Microsoft 문서의 관련 명명 규칙에서 private/internal 비상수 인스턴스 필드에 underscore 접두사를 사용하는 convention을 제시하기 때문입니다.
Q. 문서만 만들어 놓고 IDE와 CI는 사용하지 않아도 되나요?
가능하지만 자동 검증을 연결하면 사람이 규칙을 기억하는 부담을 줄일 수 있습니다. .editorconfig와 dotnet format을 함께 사용하면 같은 기준을 반복해서 적용하기 쉽습니다.
26. 최종 정리
좋은 C# Coding Style의 핵심은 특정 규칙을 외우는 것이 아니라 저장소 전체에서 같은 규칙을 재현하는 것입니다.
.NET Runtime과 Microsoft 문서에서 공백 4칸, Allman 중괄호, 명명 규칙, using 정렬, 코드 스타일 자동화 같은 다양한 기준을 확인할 수 있습니다. 그러나 실제 프로젝트에서는 저장소의 .editorconfig와 기존 코드 스타일이 우선입니다.
특히 AI 코딩 환경에서는 자연어 프롬프트만으로 스타일을 관리하기보다 .editorconfig, IDE/Analyzer, dotnet format, CI 검증을 연결하는 방식이 실무적으로 관리하기 쉽습니다.
27. 공식 출처
본 글은 2026년 9월 16일 기준으로 확인한 공식 자료를 바탕으로 정리했습니다. 코딩 스타일, .editorconfig 옵션, SDK 및 IDE 동작은 변경될 수 있습니다. 특히 실제 저장소에 .editorconfig가 있다면 해당 설정과 기존 코드 스타일을 먼저 확인하십시오.
#CSharp #CSharpCodingStyle #DotNet #DotNetRuntime #ASPNetCore #EditorConfig #DotNetFormat #VisualStudio #CodingConvention #AI코딩
댓글
댓글 쓰기