CodeGym /행동 /C# SELF /OOP 코드 스타일과 가독성에 대한 팁

OOP 코드 스타일과 가독성에 대한 팁

C# SELF
레벨 25, 레슨 4
사용 가능

1. 소개

"클린 코드"는 그냥 신성불가침한 게 아니라, 개발자가 살아남기 위한 진짜 도구야. 아무리 멋진 OOP 프로젝트라도, 금방 클래스, 필드, 메서드, 복잡한 연결이 쌓이거든… 미적 감각과 구조를 망치면, 일주일만 지나도 네 코드가 미궁이 돼버려. "생존을 위한 싸움"에 대해 더 알고 싶으면 로버트 마틴의 "클린 코드"를 읽어봐 — 딱 그런 전투 얘기야.

코드 스타일은 "각자 취향" 문제가 아니라, 모두가 편하게 살기 위한 거야:

  • 동료(혹은 너 자신)가 금방 무슨 일인지 이해할 수 있음.
  • 실수(특히 구조적인 실수)가 바로 보임.
  • 코드 수정이 쉽고, 실수도 줄어듦.

OOP 코드를 어떻게 하면 리뷰어, 동료, 그리고 린터까지 좋아하게 만들 수 있는지 같이 보자.

2. 이름짓기: 첫 번째 방어선

클래스 이름짓기

클래스는 C#에서 PascalCase로 짓는 게 국룰이야(각 단어의 첫 글자를 대문자로, 예: MyNewClass), 그리고 이름만 봐도 "이게 뭐지?"가 딱 떠올라야 해. 이름은 명사로!

public class StudentAccount { /* ... */ }
public class InvoiceGenerator { /* ... */ }

나쁜 예:

class doMagic { ... } // 나쁨: 무슨 마법? PascalCase도 아님.

메서드 이름짓기

메서드도 PascalCase로, 근데 여기선 동사 + 대상 조합이 좋아:

public void PrintReport() { ... }
public string GetFormattedName() { ... }

메서드는 동작을 나타내야 해(Print, Get, Save, Calculate 등), 읽으면서 무슨 일이 일어날지 바로 알 수 있게.

필드와 프로퍼티 이름짓기

필드는 보통 private이고, camelCase로, 언더스코어 자주 씀:

private int _count;
private Student _owner;

프로퍼티는 PascalCase로, 클래스의 외부 인터페이스니까:

public int Balance { get; set; }

변수

로컬 변수는 camelCase로, 최대한 짧고 문맥에 맞게:

string inputName;
int studentCount;

그리고 제발 a1, result2, something 같은 변수는 쓰지 마 — 다음 달에 네가 스스로 퀘스트를 만들고 싶지 않다면.

3. 클래스 구조 정리

클래스 멤버를 잘 배치하면 탐색이 쉬워지고, 뭐가 뭔지 금방 파악할 수 있어.

보통 이렇게 배치해:

  1. 생성자
  2. 프로퍼티
  3. 메서드
  4. 중첩 타입(enum, class 등)

예시:


public class Student
{
    // --- 필드 ---
    private string _name;

    // --- 생성자 ---
    public Student(string name)
    {
        _name = name;
    }

    // --- 프로퍼티 ---
    public string Name
    {
        get => _name;
        set => _name = value;
    }

    // --- 메서드 ---
    public void PrintInfo()
    {
        Console.WriteLine($"이름: {_name}");
    }
}

이런 "블록"은 주석(// --- 메서드 ---)으로 구분하면 편해, 특히 큰 클래스에서. JetBrains Rider, Visual Studio 같은 IDE는 섹션을 접거나 펼치는 것도 쉬워.

4. 주석과 문서화

주석은 좋아. 근데 남발하거나 "이해 안 되는 코드 설명"만 달면 오히려 별로야 — 그럴 땐 코드를 바꾸는 게 낫지!

좋은 주석은 "왜"를 설명하는 거지, "뭐"를 설명하는 게 아니야.

// Guid를 유니크 아이디로 씀. 시스템이 분산되어 있어서.
public Guid Id { get; set; }

메서드, 클래스, 프로퍼티 문서화

클래스랑 public 메서드는 xml 문서화 써줘. IDE에서 마우스 올리면 설명이 바로 보여.


/// <summary>
/// 대학생을 나타냄.
/// </summary>
public class Student
{
    /// <summary>
    /// 학생 이름.
/// </summary>
    public string Name { get; set; }
}

주석 달 필요 없는 것들

  • 단순한 것들(i++ // i를 1 증가시킴).
  • 이름이 별로인 변수("// 여기서 뭔가 일어남" — 네, 근데 뭔데?).

5. 분리해서 정복하라

작은 클래스와 메서드

황금률: 한 클래스엔 한 책임만(Single Responsibility Principle, SOLID 참고). Student 클래스가 점수 관리, 이메일 처리, 시간표 관리까지 다 한다면 뭔가 잘못된 거야.

  • 클래스는 300~400줄까지는 괜찮아. 그 이상이면 고민해봐.
  • 메서드는 15~20줄까지가 읽기 좋아. 예외는 큰 케이스 처리 메서드 정도.

비대해진 메서드 예시:


public void Process()
{
    // 클라이언트 알림
    // 변경사항 저장
    // 이메일 전송
    // 로그 기록
    // ... (15단계)
}

더 나은 방법:


public void Process()
{
    NotifyClient();
    SaveChanges();
    SendEmail();
    LogActivity();
}

각 단계는 따로 private 메서드로 빼면 코드가 더 깔끔하고, 테스트도 쉬워져.

6. 유용한 팁

비주얼 구조: 포맷팅, 들여쓰기, 빈 줄

IDE가 코드 예쁘게 자동 포맷팅 해주긴 하지만(Ctrl+K, D in Visual Studio, Ctrl+Alt+L in Rider), 기본 원칙은 알아둬야 해.

  • 들여쓰기 — 4칸 스페이스. 탭이나 2칸 아님.
  • 빈 줄 — 메서드끼리, 필드와 프로퍼티, 프로퍼티와 메서드 사이에 넣어줘.
  • 중괄호는 클래스, 메서드에서 항상 새 줄에(올맨 스타일):

public class Test
{
    public void Print()
    {
        Console.WriteLine("Hello");
    }
}

"강한" vs "약한" 클래스 멤버: 접근 제한자

항상 최대한 닫아두는 게 좋아: 진짜 외부에서 써야 할 것만 열어. 필드나 메서드가 클래스 내부에서만 필요하면 private로. 상속받는 쪽만 필요하면 protected. public은 진짜 외부 계약용만.

나쁜 예:

public string ConnectionString; // 아무나 바꿀 수 있음!

더 나은 예:


private string _connectionString;
public string ConnectionString
{
    get => _connectionString;
    private set => _connectionString = value;
}

자동 프로퍼티 사용하기

자동 프로퍼티랑 init-only 세터가 생긴 이후로, 직접 프로퍼티 쓰는 건 좀 구식이야.

예시:


public string Name { get; set; } // 굿!
public int Age { get; init; }    // 초기화 전용, 더 안전함.

계산 프로퍼티가 필요하면:


public string FullName => $"{FirstName} {LastName}";

캡슐화와 getter/setter

프로퍼티에 뭔가 비즈니스 로직이나 제어가 필요하면, private 필드 + public getter/setter에 로직을 넣어.


private int _grade;
public int Grade
{
    get => _grade;
    set
    {
        if (value < 0) _grade = 0;
        else if (value > 100) _grade = 100;
        else _grade = value;
    }
}

이렇게 하면 실수로 오브젝트를 망가뜨릴 일이 없어.

7. 추가 팁

인터페이스와 추상화, 겁내지 마!

인터페이스는 계약, 테스트, 확장성에 필수야.

나쁨:

  • 메서드 하나만 있는 인터페이스, 아무 데도 안 쓰임;
  • 한 클래스만 구현하는 인터페이스.
좋음:
  • 2개 이상 클래스에서 쓰는 인터페이스;
  • 외부 시스템 추상화용 인터페이스(예: 로깅, 데이터 저장 등).

테스트하기 쉬운 코드 쓰기

좋은 OOP 코드의 특징 중 하나가 테스트 용이성이야.

  • 글로벌 변수나 static 필드에 의존하는 메서드는 만들지 마.
  • 생성자 파라미터로 의존성 주입(Dependency Injection) 겁내지 마.
  • 계산(로직)과 사용자 상호작용(입출력)은 분리해. 이러면 테스트도, 유지보수도 쉬워져.

초보자를 위한 꿀팁 & 안티패턴

  • "신 객체"(God Object) 만들지 마 — 모든 걸 다 하는 클래스.
  • 설명 없는 "매직 넘버" 쓰지 마(if (status == 42) — 42가 왜?).
  • 상속만을 위한 상속 남발하지 마 — 때론 컴포지션(다른 클래스 필드로 갖기)이 더 나아.
  • 100줄짜리 복잡한 메서드는 쓰지 마 — 테스트도, 이해도 불가.
  • 확장성은 항상 열어둬(open/closed principle).

8. 나쁜 코드 vs 좋은 코드

나쁜 예:


class s // 나쁨: 클래스 이름이 소문자.
{
    public int a; // 의미 없음, 이름도 별로
    public void m() // 나쁨: 한 글자짜리 메서드 이름.
    {
        Console.WriteLine(a);
        // 나쁨: 메서드가 뭘 하는지 모름.
    }
}

좋은 예:


// 학생을 나타냄.
public class Student
{
    // 학생 나이.    
    private int _age;
    public int Age
    {
        get => _age;
        set => _age = value < 0 ? 0 : value;
    }

    // 학생 정보 출력.
    public void PrintInfo()
    {
        Console.WriteLine($"학생 나이: {Age}");
    }
}
2
과제
C# SELF, 레벨 25, 레슨 4
잠금
클래스와 메서드 네이밍 규칙 지키기
클래스와 메서드 네이밍 규칙 지키기
2
과제
C# SELF, 레벨 25, 레슨 4
잠금
클래스 구조 구성하기
클래스 구조 구성하기
1
설문조사/퀴즈
상속에서의 실수들, 레벨 25, 레슨 4
사용 불가능
상속에서의 실수들
클래스랑 객체 선언할 때 자주 하는 실수들
코멘트
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION