내일배움캠프 - TIL/내일배움캠프 - TIL

내일배움캠프 3일차 TIL 2주차 강의 1~4

rudals4469 2025. 4. 14. 20:55

잊어버리기 쉬운 중요 개념 정리

이번 포스트에서는 2주차 강의에서 들었던 개념들을 내가 헷갈릴만한 내용들을 다시 한 번 정리하는 시간을 가져보겠다.

이 외에도 더 많은 개념들이 있지만 오늘 포스트에선 쉬운 내용일 순 있지만 확실히 짚고 넘어가야 하는 키워드만 모아보았다.

  1. 변수 규칙
  2. 이스케리이 시퀀스 예시
  3. 비트 연산자
  4. 생성자, 소멸자
  5. 프로퍼티
  6. 상속
  7. 다형성
  8. 인터페이스
  9. 열거형

위의 총 9개 정도의 개념을 정리 해볼려고 한다.

 

1. 변수 규칙

변수는 몇 가지 규칙을 지켜 변수명을 만들어야 한다.

반드시 지켜야 하는 것도 있고, 코드의 가독성을 높이기 위해 지켜야 되는 경우도 있다.

  • 키워드
    • C#에서 이미 예약된 단어들은 변수, 메소드, 클래그 등의 이름으로 사용할 수 없다.
    • 다음과 같은 키워드가 있다.
abstract  as  base  bool  break  byte  case  catch  char  checked  class  const  continue 
decimal  default  delegate  do  double  else  enum  event  explicit  extern  false  finally 
fixed  float  for  foreach  goto  if  implicit  in  int  interface  internal  is  lock long 
namespace  new  null  object  operator  out  override  params  private  protected  public 
readonly  ref  return  sbyte  sealed  short  sizeof  stackalloc  static  string  struct  
switch this  throw  true  try  typeof  uint  ulong  unchecked  unsafe  ushort  using  
virtual  void volatile  while
  • 식별자
    • 식별자란 변수, 메서드, 클래스, 인터페이스 등에서 사용되는 이름.
    • 식별자를 사용할 때에는 다음과 규칙을 따라야 한다.
      • 첫 문자는 알파벳, 언더스코어가 올 수 없다.
      • 두번째 문자부터는 알파벳, 언더스코어, 숫자가 올 수 있다.
      • 대소문자를 구별한다.
      • 키워드와 같은 이름으로 사용할 수 없다.
// 좋은 예시
int playerScore;
string playerName;
float itemPrice;

// 나쁜 예시 (중요 의미 있는 변수명 짓기)
int x1;  // 변수명이 의미를 알기 어려움
string a; // 변수명이 명확하지 않음

// 오류 예시
int 1stNumber;  // 변수명은 숫자로 시작할 수 없음
string my-name; // 변수명에 하이픈(-)을 사용할 수 없음
float total$;   // 변수명에 특수문자($)를 사용할 수 없음
  • 코드 컨벤션
    • 코드 컨벤션은 개발자 사이에서 약속된 코드 작성 규칙이다.  
    • 식별자 표기법
      • PascalCase : 단어의 첫 글자는 대문자로 시작하며, 이후 단어의 첫 글자로 대문자로 표기
        • ex: ClassName, MethodName, PropertyName
      • camelCase : 단어의 첫 글자는 소문자로 시작, 이후 단어의 첫 글자를 대문자로 표기
        • ex) variableName, parameterName, localVariableName
      • 대문자 약어 : 예외적으로 전체 글자가 모두 대문자인 식별자도 있다.
        • ex) ID, HTTP, FTP
    • 들여쓰기
      • 탭 or 스페이스바 4칸을 사용하여 코드 블록을 들여쓴다.
    • 중괄호 위치
      • 중괄호는 항상 새로운 줄에서 시작한다.
    • 빈 줄 사용
      • 관련 없는 코드 사이에는 빈 줄을 사용하여 구분한다.
    • 주석

2. 이스케이프 시퀀스 예시

이스케이프 시퀀스란 문자열 내에 특수한 문자를 포함시키기 위해 사용되는 특별한 문자 조합이다.

다음은 일부 흔하게 사용되는 이스케이프 시퀀스의 예시이다.

 

이스케이프 시퀀스 설명
\' 작은따옴표(') 삽입
\" 큰따옴표(") 삽입
\\ 역슬래시() 삽입
\n 새 줄(줄바꿈) 삽입
\r 현재 줄 맨 앞으로 이동
\t 탭 삽입
\b 백스페이스 삽입

 

종종 까먹어 정리를 한 번 해보았다.

3. 비트 연산자

비트 연산자는 데이터의 비트(bit) 단위로 연산을 수행하는 연산자이다.

 

연산자 설명
& 두 비트 값이 모두 1일 때 1을 반환
| 두 비트 값 중 하나라도 1일 때 1을 반환
^ 두 비트 값이 서로 다를 때 1을 반환
~ 비트 값의 보수(complement)를 반환
<< 비트를 왼쪽으로 이동
>> 비트를 오른쪽으로 이동

4. 생성자, 소멸자

클래스는 생성자와 소멸자라는 특별한 종류의 메서드를 가질 수 있다.

생성자는 클래스의 인스턴스를 생성할 때 호출되는 메서드이며,

소멸자는 클래스의 인스턴스가 메모리에서 해제될 때 호출되는 메서드이다.

  • 생성자
    • 생성자란?
      • 생성자는 객체가 생성될 때 호출되는 특별한 메서드이다.
      • 클래스의 인스턴스(객체)를 초기화하고, 필요한 초기값을 설정하는 역할을 수행한다.
      • 생성자는 클래스와 동일한 이름을 가지며, 반환타입이 없다.
      • 객체를 생성할 때 new 키워드와 함께 호출된다.
    • 생성자의 특징
      • 객체를 초기화하는 과정에서 필요한 작업을 수행할 수 있다.
      • 생성자는 여러 개 정의가 가능하며, 매개변수의 개수와 타입에 따라 다른 생성자를 호출 할 수 있다(생성자 오버로딩)
      • 기본적으로 매개변수가 없는 디폴트 생성자가 자동으로 생성되지만, 사용자가 직접 정의한 생성자가 있는 경우 디폴트 생성자가 자동으로 생성되지 않는다.
class Person
{
    private string name;
    private int age;

    // 매개변수가 없는 디폴트 생성자
    public Person()
    {
        name = "Unknown";
        age = 0;
    }

    // 매개변수를 받는 생성자
    public Person(string newName, int newAge)
    {
        name = newName;
        age = newAge;
    }

    public void PrintInfo()
    {
        Console.WriteLine($"Name: {name}, Age: {age}");
    }
}

    Person person1 = new Person();                     // 디폴트 생성자 호출
    Person person2 = new Person("John", 25);           // 매개변수를 받는 생성자 호출
  • 소멸자
    • 소멸자란?
      • 객체가 소멸되는 시점에서 자동으로 호출되는 특별한 메서드
      • 객체의 사용이 종료되고 메모리에서 해제될 때 자동으로 호출되어 필요한 정리 작업을 수행한다.
      • 클래스와 동일한 이름을 가지며, 앞에 ~ 기호를 붙혀 표현한다
      • 소멸자는 반환 타입이 없고 매개변수를 가질 수 없다.
      • C#에서는 가비지 컬렉터에 의해 관리되는 메모리 해제를 담당하므로, 명시적으로 호출하는 것은 권장되지 않는다.
    • 역할
      • 자원 해제 : 파일 핸들, 네트워크 연결, 데이터베이스 연걸 등의 외부 리소스를 사용한 경우 해당 리소스를 해제한다.
      • 메모리 해제 : 객체가 사용한 메모리를 해제하고 관련된 자원을 정리할 수 있다.
      • 로깅 및 디버깅 : 객체가 소멸되는 시점에 로깅 작업을 수행하거나 디버깅 정보를 기록할 수 있다.
class Person
{
    private string name;

    public Person(string newName)
    {
        name = newName;
        Console.WriteLine("Person 객체 생성");
    }

    ~Person()
    {
        Console.WriteLine("Person 객체 소멸");
    }
}

5. 프로퍼티

  • 프로퍼티란?
    • 클래스 멤버로서, 객체의 필드 값을 읽거나 설정하는데 사용되는 접근자 메서드의 조합이다.
    • 객체의 필드에 직접 접근하지 않고, 간접적으로 값을 설정하거나 읽을 수 있도록 한다.
    • 필드에 대한 접근 제어와 데이터 유효성 검사 등을 수행할 수 있다.
    • 필드와 마찬가지로 객체의 상태를 나타내는 데이터 역할을 하지만, 외부에서 접근할 때 추가적인 로직을 수행할 수 있다.
  • 프로퍼티 구문
    • get과 set 접근자를 사용하여 값을 읽고 설정하는 동작을 정의한다.
    • get 접근자는 프로퍼티의 값을 반환하고, set 접근자는 프로퍼티의 값을 설정한다.
    • 필요에 따라 get 또는 set 접근자 중 하나를 생략하여 읽기 전용 또는 쓰기 전용 프로퍼티를 정의할 수 있다.
[접근 제한자] [데이터 타입] 프로퍼티명
{
    get
    {
        // 필드를 반환하거나 다른 로직 수행
    }
    set
    {
        // 필드에 값을 설정하거나 다른 로직 수행
    }
}
  • 프로퍼티 사용 예시
    • 다음은 Person의이름과 나이를 나타내는 name과 age 필드를 캡슐화한 프로퍼티의 예시이다.
class Person
{
    private string name;
    private int age;

    public string Name
    {
        get { return name; }
        set { name = value; }
    }

    public int Age
    {
        get { return age; }
        set { age = value; }
    }
}

    Person person = new Person();
    person.Name = "John";   // Name 프로퍼티에 값 설정
    person.Age = 25;        // Age 프로퍼티에 값 설정

    Console.WriteLine($"Name: {person.Name}, Age: {person.Age}");  // Name과 Age 프로

 

  • 프로퍼티 접근 제한자 적용 & 유효성 검사 예제
class Person
{
    private string name;
    private int age;

    public string Name
    {
        get { return name; }
        private set { name = value; }
    }

    public int Age
    {
        get { return age; }
        set
        {
            if (value >= 0)
                age = value;
        }
    }
}

    Person person = new Person();
    person.Name = "John";     // 컴파일 오류: Name 프로퍼티의 set 접근자는 private입니다.
    person.Age = -10;         // 유효성 검사에 의해 나이 값이 설정되지 않습니다.

    Console.WriteLine($"Name: {person.Name}, Age: {person.Age}");  // Name과 Age 프로퍼티에 접근하여 값을 출력합니다.

 

  • 자동 프로퍼티
    • 자동 프로퍼티는 프로퍼티를 간단하게 정의하고 사용할 수 있는 편리한 기능이다.
    • 필드의 선언과 접근자 메서드의 구현을 컴파일러가 자동으로 처리하여 개발자가 간단하게 정의할 수 있다.
class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
}

    Person person = new Person();
    person.Name = "John";     // 값을 설정
    person.Age = 25;          // 값을 설정

    Console.WriteLine($"Name: {person.Name}, Age: {person.Age}");  // 값을 읽어 출력

6. 상속

상속은 객체지향 프로그래밍에서 중요한 개념

  • 상속이란?
    • 상속의 개념
      • 상속은 기존의 클래스를 확장하거나 재사용하여 새로운 클래스를 생성하는 것이다.
      • 자식 클래스는 부모 클래스의 멤버를 상속받아 사용할 수 있다.
      • 상속을 통해 부모 클래스의 기능을 확장하거나 수정하여 새로운 클래스를 정의할 수 있다.
    • 상속의 장점
      • 코드의 재사용성 
      • 계층 구조의 표현
      • 유지보수성의 향상
    • 상속의 종류
      • 단일 상속 : 하나의 자식 클래스가 하나의 부모 클래스만 상속받는 것
      • 다중 상속 : 하나의 자식 클래스가 여러 개의 부모 클래스를 동시에 상속받는 것. ( C#에서는 지원하지 않는 기능)
      • 인터페이스 상속: 클래스가 인터페이스를 상속받는 것. 인터페이스는 다중 상속을 지원한다.
    • 상속의 특징
      • 부모 클래스의 멤버에 접근
        • 자식 클래스는 부모 클래스의 멤버에 접근할 수 있으며, 이를 통해 부모 클래스의 기능을 재사용할 수 있다.
      • 메서드 재정의
        • 자식 클래스는 부모 클래스의 메서드를 재정의하여 자신에게 맞게 수정 할 수 있다 - 다형성
      • 상속의 깊이
        • 클래스는 다수의 계층적인 상속 구조를 가질 수 있다. 부모 클래스가 또 다른 자식 클래스가 될 수 있다는 의미
        • 너무 많이 사용하는 것은 클래스 간의 복잡성을 때문에 적절한 상속의 깊이를 유지하는 것이 좋다.
    • 접근 제한자와 상속
      • 상속 관계에서는 접근 제한자가 중요한 역할을 한다.
      • 접근 제한자를 통해 상속된 멤버의 가시성을 조절하여 캡슐화정보 은닉을 구현할 수 있다.
  • 예제 코드
// 부모 클래스
public class Animal
{
    public string Name { get; set; }
    public int Age { get; set; }

    public void Eat()
    {
        Console.WriteLine("Animal is eating.");
    }

    public void Sleep()
    {
        Console.WriteLine("Animal is sleeping.");
    }
}

// 자식 클래스
public class Dog
{

}

public class Cat
{

}

7. 다형성

같은 타입이지만 다양한 동작을 수행할 수 있는 능력

  • 가상 메서드
    • 기본적으로 부모 클래스에서 정의되고 자식 클래스에서 재정의 할 수 있는 메서드이다.
    • 가상 메서드는 virtual 키워드를 사용하여 선언되며, 자식 클래스에 따라 재정의 될 수 있다.
    • 이를 통해 자식 클래스에서 부모 클래스의 메서드를 변경하거나 확장 할 수 있다.
public class Unit
{
    public virtual void Move()
    {
        Console.WriteLine("두발로 걷기");
    }

    public void Attack()
    {
        Console.WriteLine("Unit 공격");
    }
}

public class Marine : Unit
{

}

public class Zergling : Unit
{
    public override void Move()
    {
        Console.WriteLine("네발로 걷기");
    }
}

    // 사용 예시
    // #1 참조형태와 실형태가 같을때
    Marine marine = new Marine();
    marine.Move();
    marine.Attack();

    Zergling zergling = new Zergling();
    zergling.Move();
    zergling.Attack();

    // #2 참조형태와 실형태가 다를때
    List<Unit> list = new List<Unit>();
    list.Add(new Marine());
    list.Add(new Zergling());

    foreach (Unit unit in list)
    {
        unit.Move();
    }

 

  • 추상 클래스와 메서드
    • 직접적으로 인스턴스를 생성할 수 없는 클래스
    • 주로 상속을 위한 베이스 클래스로 사용된다.
    • 추상 클래스는 abstract 키워드를 사용하여 선언되며, 추상 메서드를 포함할 수 있다.
    • 구현두가 없는 메서드로, 자식 클래스에서 반드시 구현되어야 한다.
abstract class Shape
{
    public abstract void Draw();
}

class Circle : Shape
{
    public override void Draw()
    {
        Console.WriteLine("Drawing a circle");
    }
}

class Square : Shape
{
    public override void Draw()
    {
        Console.WriteLine("Drawing a square");
    }
}

class Triangle : Shape
{
    public override void Draw()
    {
        Console.WriteLine("Drawing a triangle");
    }
}

    List<Shape> list = new List<Shape>();
    list.Add(new Circle());
    list.Add(new Square());
    list.Add(new Triangle());

    foreach (Shape shape in list )
    {
        shape.Draw();
    }
  • 오버라이딩과 오버로딩
    • 오버라이딩과 오버로딩은 객체 지향 프로그래밍에서는 다른 개념을 나타내는 두 용어이다.
      • 오버 라이딩
        • 부모 클래스에서 이미 정의된 메서드를 자식 클래스에서 재정의하는 것을 의미한다.
      • 오버로딩
        • 동일한 메서드 이름을 가지고 있지만, 매개변수의 개수, 타입 또는 순서가 다른 여러 개의 메서드를 정의하는 것을 의미한다.
ublic class Shape
{
    public virtual void Draw()
    {
        Console.WriteLine("Drawing a shape.");
    }
}

public class Circle : Shape
{
    public override void Draw()
    {
        Console.WriteLine("Drawing a circle.");
    }
}

public class Rectangle : Shape
{
    public override void Draw()
    {
        Console.WriteLine("Drawing a rectangle.");
    }
}

    Shape shape1 = new Circle();
    Shape shape2 = new Rectangle();

    shape1.Draw();  // Drawing a circle.
    shape2.Draw();  // Drawing a rectangle.

-  오버라이딩 예시

public class Calculator
{
    public int Add(int a, int b)
    {
        return a + b;
    }

    public int Add(int a, int b, int c)
    {
        return a + b + c;
    }
}

Calculator calc = new Calculator();
int result1 = calc.Add(2, 3);         // 5
int result2 = calc.Add(2, 3, 4);      // 9

- 오버로딩 예시

8. 인터페이스

  • 다중 상속을 사용하지 않는 이유
    • 다이아몬드 문제
    • 설계의 복잡성 증가
    • 이름 충돌과 충돌 해결의 어려움
    • 설계의 일관성과 단순성 유지
  • 인터페이스를 사용하는 이유
    • 코드의 재사용성
    • 다중 상속 제공
    • 유연한 설계
      • 클래스와 인터페이스 간에 느슨한 결합을 형성할 수 있다.
  • 인터페이스
    • 특징
      • 클래스가 구현해야 하는 멤버들을 정의하는 것이다.
      • 클래스의 일종이 아니며, 클래스에 대한 제약 조건을 명시하는 것이다.
      • 클래스가 인터페이스를 구현할 경우, 모든 인터페이스 멤버를 구현해야 한다.
    • 구현
interface IMyInterface
{
    void Method1();
    int Method2(string str);
}

class MyClass : IMyInterface
{
    public void Method1()
    {
        // 구현
    }

    public int Method2(string str)
    {
        // 구현
        return 0;
    }
}

 

  • 사용 예제
    1. 인터페이스 정의하기
    2. 인터페이스를 구현하는 클래스 생성하기
    3. 인터페이스를 사용하여 객체 이동하기
public interface IMovable
{
    void Move(int x, int y); // 이동 메서드 선언
}
############################################################# 1

public class Player : IMovable
{
    public void Move(int x, int y)
    {
        // 플레이어의 이동 구현
    }
}

public class Enemy : IMovable
{
    public void Move(int x, int y)
    {
        // 적의 이동 구현
    }
}

############################################################# 2

    IMovable movableObject1 = new Player();
    IMovable movableObject2 = new Enemy();

    movableObject1.Move(5, 0); // 플레이어 이동
    movableObject2.Move(1, 9); // 적 이동
    
############################################################# 3
  • 아이템 사용 구현 예제
// 아이템을 사용할 수 있는 인터페이스
public interface IUsable
{
    void Use();
}

// 아이템 클래스
public class Item : IUsable
{
    public string Name { get; set; }

    public void Use()
    {
        Console.WriteLine("아이템 {0}을 사용했습니다.", Name);
    }
}

// 플레이어 클래스
public class Player
{
    public void UseItem(IUsable item)
    {
        item.Use();
    }
}

// 게임 실행
static void Main()
{
    Player player = new Player();
    Item item = new Item { Name = "Health Potion" };
    player.UseItem(item);
}
  • 다중 상속 구현 예제
// 인터페이스 1
public interface IItemPickable
{
    void PickUp();
}

// 인터페이스 2
public interface IDroppable
{
    void Drop();
}

// 아이템 클래스
public class Item : IItemPickable, IDroppable
{
    public string Name { get; set; }

    public void PickUp()
    {
        Console.WriteLine("아이템 {0}을 주웠습니다.", Name);
    }

    public void Drop()
    {
        Console.WriteLine("아이템 {0}을 버렸습니다.", Name);
    }
}

// 플레이어 클래스
public class Player
{
    public void InteractWithItem(IItemPickable item)
    {
        item.PickUp();
    }

    public void DropItem(IDroppable item)
    {
        item.Drop();
    }
}

// 게임 실행
static void Main()
{
    Player player = new Player();
    Item item = new Item { Name = "Sword" };

    // 아이템 주울 수 있음
    player.InteractWithItem(item);

    // 아이템 버릴 수 있음
    player.DropItem(item);
}
  • 인터페이스 vs 추상클래스
    • 인터페이스의 특징과 장단점
      • 인터페이스는 추상적인 동작만 정의하고, 구현을 갖기 않는다
      • 다중 상속이 가능하며, 여러 클래스가 동일한 인터페이스를 구현할 수 있다.
      • 클래스 간의 결합도를 낮추고, 유연한 상호작용을 가능하게 한다.
      • 코드의 재사용성과 확장성을 향상시킨다.
      • 인터페이스를 구현하는 클래스가 모든 동작을 구현해야 한다는 의무가 있기 때문에 작업량이 증가할 순 있다.
    • 추상 클래스의 특징과 장단점
      • 추상 클래스는 일부 동작의 구현을 가지며, 추상 메서드를 포함할 수 있다.
      • 단일 상속만 가능하며, 다른 클래스와 함께 상속 계층 구조를 형성할 수 있다.
      • 공통된 동작을 추상화하여 코드의 중복을 방지하고 확장성을 제공한다.
      • 구현된 동작을 가지고 있기 때문에, 하위 클래스에서 재정의하지 않아도 될 경우 유용하다.
      • 다중 상속이 불가능하고, 상속을 통해 밀접하게 결합된 클래스들을 형성하므로 유연성이 제한될 순 있다.

9. 열거형

  • 사용하는 이유
    • 가독성
    • 자기 문서화
    • 스위치 문과의 호환성
  • 특징
    • 서로 관련된상수들의 집합을 정의할 때 사용된다.
    • 열거형의 각 상수는 정수 값으로 지정된다.
  • 예제 - 입력 월 출력
// 월 열거형
public enum Month
{
    January = 1,
    February,
    March,
    April,
    May,
    June,
    July,
    August,
    September,
    October,
    November,
    December
}

// 처리하는 함수
static void ProcessMonth(int month)
{
    if (month >= (int)Month.January && month <= (int)Month.December)
    {
        Month selectedMonth = (Month)month;
        Console.WriteLine("선택한 월은 {0}입니다.", selectedMonth);
        // 월에 따른 처리 로직 추가
    }
    else
    {
        Console.WriteLine("올바른 월을 입력해주세요.");
    }
}

// 실행 예제
static void Main()
{
    int userInput = 7; // 사용자 입력 예시
    ProcessMonth(userInput);
}

 

회고

오늘은 이번 주 주차의 강의 진도를 최대한 빼놓는다고 강의 내용이 너무 많았다.

아직 정리할 개념들이 조금 남았지만 이 포스터에 한대 놓긴 힘들 거 같아 분량을 2개로 나눴다.

분명 애매한 부분들만 정리한다고 간추린 건데 왤캐 양이 많은 것이야..

그리고 왜 이렇게 진도를 쫙 빼놨냐 하면은

이번 주에 해야 할 과제가 상당히 재밌는 부분이 많을 것 같아서 빨리 구현해보고 싶었기 때문이다.

이제 내일 강의 마무리 하고 과제에 거의 모든 시간을 할애할 수 있게 되었다.