no image
C# - Class Inheritance(클래스 상속)& Interface(인터페이스) vs abstract Class
✅ 클래스 상속 (Inheritance)✔️ 정의class A : B처럼, 한 클래스가 다른 클래스의 기능을 물려받는 것✔️ 목적코드 재사용: 부모 클래스의 멤버(필드, 메서드 등)를 자식이 그대로 사용 가능기능 확장: 자식 클래스는 새로운 기능을 추가하거나, 부모 기능을 재정의 가능다형성(polymorphism): 부모 타입으로 자식 객체를 다룰 수 있음✔️ 예제class Character{ public virtual void Attack() { Console.WriteLine("기본 공격!"); }}class Warrior : Character{ public override void Attack() { Console.WriteLine("칼 휘두르기!..
2025.07.03
C#
no image
C# - Virtual & abstract
✅ virtual 키워드 정리📌 정의virtual은 부모 클래스에서 메서드나 속성을 "재정의 가능"하게 만드는 키워드입니다.📋 기본 문법class Parent{ public virtual void SayHello() { Console.WriteLine("안녕하세요. 부모입니다."); }}class Child : Parent{ public override void SayHello() { Console.WriteLine("안녕하세요. 자식입니다."); }}✅ 주요 특징기본 구현 제공부모 클래스가 기본 로직을 정의자식 클래스에서 변경 가능override 키워드로 오버라이딩 가능선택적 재정의자식 클래스에서 반드시 재정의하지 않아도 됨다형성 지원부모 타..
2025.07.03
C#
no image
TIL - 내일배움캠프 3일차 TIL [CS공부 + 카드게임 팀 프로젝트](25.07.02)
오늘 하루 계획오전09:00 ~ 13:00 : CS 공부어제 내용 복습 - GC, 해시테이블, 딕셔너리, .NET 런타임, 캡슐화, 박싱/언박싱 & 형변환HashTableDictionaryHashSet제네릭 (Generics)ScriptableObject오후13:00 ~ 14:00 : 점심시간14:00 ~ 18:00 : 팀 프로젝트 진행 (12조)폭탄 스킬 기능 구현 및 사운드 연결카드 수량에 따라 3x3 / 4x4 자동 배치 처리게임오버 사운드 추가GameManager, Card, Board 등 스크립트 리팩토링ScriptableObject Stage 데이터 기반 동작 적용 완료18:00 ~ 19:00 : 저녁시간저녁19:00 ~ 20:00 : 팀 회의20:00 ~ 20:30 : 개인 TIL 작성20:..
2025.07.02
no image
C# - 자료구조
✅ 자료구조란?자료(데이터)를 효율적으로 저장하고, 관리하고, 사용할 수 있도록 구성한 방식쉽게 말하면,"데이터를 어떻게 정리해서 넣고, 꺼내고, 수정할지에 대한 규칙과 구조"✅ 왜 중요한가?빠르게 찾기 위해효율적으로 저장하기 위해복잡한 데이터를 쉽게 다루기 위해성능 좋은 알고리즘을 만들기 위해✅ 비유로 설명하면자료구조 = 서랍장예를 들어, 옷을 아무데나 넣으면 나중에 찾기 힘들지?자료구조는 "양말은 맨 위 칸", "바지는 아래 칸"처럼 정리 기준을 정해주는 거야.✅ 주요 자료구조 종류 (기초)자료구조설명예시배열 (Array)순서대로 데이터를 저장리스트, 숫자 모음리스트 (List)동적으로 크기가 변하는 배열C#의 List스택 (Stack)나중에 넣은 게 먼저 나옴 (LIFO)웹 브라우저 뒤로가기큐 (Q..
2025.07.02
C#
no image
C# - 해쉬셋(HashSet) vs Dictionary(HashTable)
✅ HashSet이란?중복을 허용하지 않는 데이터 집합 (Set)을 구현한 자료구조내부적으로는 **해시 테이블(Hash Table)**을 사용하지만,핵심 목적은 **"중복 없는 값의 모음"**을 만드는 거야.🔍 HashSet의 특징❌ 중복 허용 안 함같은 값을 두 번 넣어도 한 번만 저장됨🔍 순서 없음넣은 순서 보장 안 됨⚡ 매우 빠름추가, 삭제, 탐색 전부 평균 O(1)📦 내부 구조키만 있고, 값은 없음 (딕셔너리는 key-value, 얘는 key만 존재)✅ HashSet 예제 (C#)using System.Collections.Generic;HashSet names = new HashSet();names.Add("Alice");names.Add("Bob");names.Add("Alice"); ..
2025.07.02
C#
no image
Unity - 스크립터블오브젝트(ScriptableObject)&예제
✅ ScriptableObject란?📌 개념 정리ScriptableObject는 MonoBehaviour처럼 Unity에서 사용하는 클래스지만,씬에 붙는 컴포넌트가 아니고, 독립적인 데이터 객체야.데이터를 자산(Asset)으로 만들어 저장하고 공유할 수 있는 방식을 제공해.✅ 언제 사용하나요?여러 객체가 동일한 데이터를 참조할 때예: 스탯, 설정값, 아이템 정보 등프리팹이나 코드와 독립적으로 데이터 관리하고 싶을 때설정 변경이 쉽고 유지보수 편함인스펙터에서 직접 데이터 입력하고 저장하고 싶을 때커스텀 에디터 연동도 쉬움✅ 간단한 예제: 캐릭터 능력치 데이터 만들기1. CharacterStats.csusing UnityEngine;[CreateAssetMenu(fileName = "NewCharacter..
2025.07.02
no image
C# - 해시테이블(HashTable) vs 딕셔너리(Dictionary)&제네릭(Generic)
✅ 해시테이블 (Hash Table)📌 개념키(key)를 해시 함수(Hash Function)에 넣어 나온 해시값을 기반으로 데이터를 저장하는 자료구조.내부적으로는 배열(array)와 연결 리스트 또는 트리 구조를 함께 사용해 충돌을 해결함.📌 동작 방식Key → 해시 함수 → 인덱스 계산계산된 인덱스에 값을 저장같은 인덱스에 이미 값이 있으면 충돌(Collision) 발생충돌 해결법: 체이닝(Chaining), 오픈 어드레싱(Open Addressing)📌 시간 복잡도 (평균적 상황 기준)삽입: O(1)삭제: O(1)탐색: O(1)※ 최악의 경우에는 O(n) (모든 키가 같은 인덱스로 해시됨)✅ 딕셔너리 (Dictionary in C#)📌 개념C#에서의 Dictionary는 해시테이블 기반으로 ..
2025.07.02
C#
no image
TIL - 내일배움캠프 2일차 TIL [CS공부 + 카드게임 팀 프로젝트](25.07.01)
1. 오늘 학습 키워드Unity, Card 시스템, 애니메이션 제한, 클릭 제한, Git 협업, Detached HEAD, ScriptableObject 개념, Update 주기, GC, 캡슐화2. 오늘 학습 한 내용을 나만의 언어로 정리하기 오전에는 컴퓨터 사이언스 관련 기초 개념을 정리했다.Unity에서의 Update, FixedUpdate, LateUpdate 주기 차이를 이해하고객체지향 원칙 중 하나인 **캡슐화(Encapsulation)**에 대해 복습했다.C#에서 **GC(Garbage Collector)**의 동작 조건과 실행 시점도 정리했으며,값 형식과 참조 형식의 차이를 통해 메모리 구조와 호출 방식을 구분했다.또한 C#의 박싱(Boxing)과 언박싱(Unboxing) 개념을 실습하며 성..
2025.07.01
반응형

✅ 클래스 상속 (Inheritance)

✔️ 정의

class A : B처럼, 한 클래스가 다른 클래스의 기능을 물려받는 것

✔️ 목적

  • 코드 재사용: 부모 클래스의 멤버(필드, 메서드 등)를 자식이 그대로 사용 가능
  • 기능 확장: 자식 클래스는 새로운 기능을 추가하거나, 부모 기능을 재정의 가능
  • 다형성(polymorphism): 부모 타입으로 자식 객체를 다룰 수 있음

✔️ 예제

class Character
{
    public virtual void Attack()
    {
        Console.WriteLine("기본 공격!");
    }
}

class Warrior : Character
{
    public override void Attack()
    {
        Console.WriteLine("칼 휘두르기!");
    }
}
Character c = new Warrior();
c.Attack();  // 출력: 칼 휘두르기!

➡️ 부모 타입으로 자식 인스턴스를 다룰 수 있다 = 다형성


⚠️ 단점

  • 단일 상속만 가능: 부모는 한 명만
  • 상속 깊이가 깊어지면 유지보수가 어려워짐
  • 상속 관계가 강한 결합을 만들 수 있음 (부모 변경 시 자식에 영향 큼)

✅ 인터페이스 (Interface)

✔️ 정의

interface IAttackable처럼, 기능의 "약속"만 정의하는 타입
구현 없이 메서드 시그니처만 선언
→ 구현은 클래스에서 직접 해야 함


✔️ 목적

  • 다형성 지원: 특정 인터페이스를 구현한 객체들을 동일하게 다룸
  • 의존성 분리: 구체 타입보다 "역할"에 의존
  • 다중 상속 가능: 하나의 클래스가 여러 인터페이스를 동시에 구현 가능

✔️ 예제

 
interface IAttackable
{
    void Attack();
}

class Archer : IAttackable
{
    public void Attack()
    {
        Console.WriteLine("화살 발사!");
    }
}
IAttackable a = new Archer();
a.Attack();  // 출력: 화살 발사!

➡️ 누구든 IAttackable만 구현하면 같은 방식으로 다룰 수 있음 → 유연함, 약한 결합


✅ 클래스 상속 vs 인터페이스 정리

항목 클래스 상속 인터페이스
목적 기능 물려주기 (재사용) 역할 정의 (규칙 강제)
코드 재사용 ✅ 가능 ❌ 없음 (직접 구현 필요)
다중 상속 ❌ 안 됨 ✅ 가능
구현 제공 ✅ 가능 (virtual, 필드 등) ❌ 없음 (단순 시그니처)
추상화 수준 구체적 (기능 포함) 완전 추상적
설계 유연성 낮음 (강한 결합) 높음 (약한 결합)

그럼 클래스의 abstract와 인터페이스는 좀 비슷한거 아니야? 라고 생각이 들었다. 

 

💯 딱 그거야!
많은 사람들이 처음에 “abstract class랑 interface… 비슷한데 뭐가 달라?” 라고 궁금해해.
왜냐면 둘 다 “강제 구현”을 요구하고, 직접 인스턴스를 만들 수 없고, 자식이 override 해야 하니까 헷갈릴 수 있어.

근데! 실제로는 의도와 사용 목적이 분명하게 다르기 때문에


**"언제 abstract class를 쓰고 언제 interface를 쓰는가?"**가 중요한 핵심이야.

✅ 공통점

인스턴스 생성 불가 abstract class, interface 모두 직접 new로 못 만듦
메서드 강제 구현 자식 클래스가 반드시 override해야 하는 메서드 있음
다형성 지원 부모 타입으로 자식 인스턴스를 다룰 수 있음 (Animal a = new Dog())

⚔️ 차이점

항목 abstract class interface
목적 공통 로직 공유 + 일부 구현 강제 **기능 명세(역할)**만 정의
기본 구현 제공 ✅ 가능 (필드, 메서드, 속성 등) ❌ 원칙상 불가능 (C# 8부터 일부 가능하긴 함)
필드 선언 ✅ 가능 ❌ 불가능
생성자 ✅ 가질 수 있음 ❌ 없음
상속 개수 ❌ 단일 상속만 가능 ✅ 다중 상속 가능
접근 제한자 ✅ 사용 가능 (protected, private, 등) ❌ 기본적으로 public only

🎮 메이플스토리 비유로 다시 정리!

abstract class 공통 기능 + 각 직업 전용 스킬: 기본 체력 시스템, 레벨업 시스템 등 공유됨 + 각 전직 클래스가 고유 스킬 구현
interface 공격 가능한 존재: 플레이어, 몬스터, 보스가 각각 다른 방식으로 Attack() 구현함 → 역할만 공유함

✅ 언제 abstract class를 쓰고 언제 interface를 써?

공통 로직도 공유해야 함 ✅ abstract class
여러 클래스가 같은 기능을 수행하지만, 내부 로직은 각자 다르게 하고 싶을 때 ✅ interface
여러 개의 기능(역할)을 동시에 구현해야 함 ✅ interface
기본 멤버/필드/생성자도 물려줘야 함 ✅ abstract class

🧠 기억하기 좋은 한 줄 요약

interface는 “무엇을 할 수 있는가”를 말하고,
abstract class는 “무엇을 공유할 것인가”를 말한다.

 

흠.. 클래스 상속과 인터페이스는 알겠는데 아직까지 솔직히 abstract Class와 Interface의 차이가 이론적으로는 알겠지만 마음으로 와닿지 않는다 이건 직접 유니티로 구현해보면서 경험을 쌓는 수 밖에 없을 듯 하다.. 

반응형

'C#' 카테고리의 다른 글

C# - Parent obj = new Child(); 메모리는 어떻게 될까? & 다형성  (1) 2025.07.03
C# - base 키워드  (3) 2025.07.03
C# - Virtual & abstract  (0) 2025.07.03
C# - 자료구조  (1) 2025.07.02
C# - 해쉬셋(HashSet) vs Dictionary(HashTable)  (2) 2025.07.02

C# - Virtual & abstract

Dev_Jen
|2025. 7. 3. 10:04
반응형

✅ virtual 키워드 정리

📌 정의

virtual은 부모 클래스에서 메서드나 속성을 "재정의 가능"하게 만드는 키워드입니다.


📋 기본 문법

class Parent
{
    public virtual void SayHello()
    {
        Console.WriteLine("안녕하세요. 부모입니다.");
    }
}

class Child : Parent
{
    public override void SayHello()
    {
        Console.WriteLine("안녕하세요. 자식입니다.");
    }
}

✅ 주요 특징

기본 구현 제공 부모 클래스가 기본 로직을 정의
자식 클래스에서 변경 가능 override 키워드로 오버라이딩 가능
선택적 재정의 자식 클래스에서 반드시 재정의하지 않아도 됨
다형성 지원 부모 타입으로 자식 객체를 가리킬 때, 실제 자식의 메서드가 실행됨

🔍 예시

 
Parent obj1 = new Parent();
obj1.SayHello();  // 출력: 안녕하세요. 부모입니다.

Child obj2 = new Child();
obj2.SayHello();  // 출력: 안녕하세요. 자식입니다.

Parent obj3 = new Child();
obj3.SayHello();  // 출력: 안녕하세요. 자식입니다. (다형성!)

🧠 언제 쓸까?

  • 기본 동작은 있으나, 자식 클래스에서 상황에 따라 동작을 바꾸고 싶을 때
  • 템플릿 메서드 패턴에서 일부 단계를 virtual로 두면 자식 클래스가 필요 시 바꿀 수 있음

❗ 주의

  • virtual 메서드는 자식 클래스에서 override 키워드로 재정의해야
  • virtual 없이 그냥 override는 불가능함 → 부모가 virtual 또는 abstract여야 함

 

그럼 궁금한게 꼭 버츄얼을 써야 오버라이드가 가능한건가? 싶기도하고 abstract는 또 뭘까 싶었다.

 

✅ 1. 꼭 virtual을 써야 override가 가능한가?

👉 네, 맞습니다.

  • override 키워드는 반드시 부모 클래스에서 virtual, abstract, 또는 override로 선언된 메서드만 재정의할 수 있어요.
  • 즉, virtual 없이 그냥 만든 메서드는 자식 클래스에서 override 할 수 없습니다.
class Parent
{
    public void Speak()  // virtual이 아님
    {
        Console.WriteLine("부모가 말합니다");
    }
}

class Child : Parent
{
    // ❌ 오류! override 불가능
    public override void Speak()
    {
        Console.WriteLine("자식이 말합니다");
    }
}

✅ 2. 그럼 abstract는 뭐야?

👉 abstract는 부모 클래스에서 "이건 반드시 자식 클래스가 직접 구현해야 해!" 라고 강제할 때 사용해요.

📌 특징 비교

  

구분 virtual abstract
기본 구현 ✅ 있음 ❌ 없음 (구현 금지)
재정의 여부 자식 클래스에서 재정의 선택 가능 자식 클래스에서 반드시 override 해야 함
용도 "기본 구현은 있지만, 필요하면 바꿔 써도 돼" "이건 자식이 반드시 구현해야 돼"

 


🎯 코드 예시로 완전 비교

🔹 virtual 예시

class Parent
{
    public virtual void Greet()
    {
        Console.WriteLine("Hello from Parent");
    }
}

class Child : Parent
{
    public override void Greet()
    {
        Console.WriteLine("Hello from Child");
    }
}
 

🔹 abstract 예시

abstract class Animal
{
    public abstract void Speak();  // 구현 ❌
}

class Dog : Animal
{
    public override void Speak()
    {
        Console.WriteLine("멍멍!");
    }
}

→ Speak()는 무조건 자식이 override 해야 함.
→ 안 하면 컴파일 오류 발생


🧠 요약 정리

질문답변
virtual 없이 override 가능? ❌ 불가능 (virtual, abstract, 또는 override 필요)
abstract는 뭐야? 자식 클래스가 반드시 구현해야 하는 메서드를 정의할 때 사용
virtual과 abstract 차이는? 기본 구현 유무 + 오버라이드 강제 여부의 차이

그러면 굳이 abstract를 써야하는 이유가있나?..싶었다..

그럼 도대체 두개의 차이점이 뭘까..

 

✅ 핵심 요약: 둘의 의도와 차이

 

항목 virtual abstract
기본 구현 있다 → 자식이 그대로 써도 됨 없다 → 자식이 반드시 구현해야 함
자식에서 override 선택 가능 강제
설계 의도 "기본 동작이 있지만, 필요하면 바꿔 써" "이건 네가 알아서 반드시 만들어"
목적 유연성 강제성, 명확한 계약
사용 가능 클래스 일반 클래스, 추상 클래스 추상 클래스에서만 사용 가능
장점 코드 재사용 가능 설계 실수 방지, 역할 명확
단점 재정의 안 하면 예측 어려움 반복적 구현 필요 (유지비 증가)
 

🎯 예시 비교로 감 잡기

✅ 예제 상황: "게임 캐릭터"

1. virtual 예시

class Character
{
    public virtual void Attack()
    {
        Console.WriteLine("기본 공격: 펀치!");
    }
}

class Knight : Character
{
    public override void Attack()
    {
        Console.WriteLine("검으로 공격!");
    }
}

class Villager : Character
{
    // Attack() 안 바꿔도 됨 → 기본 공격 그대로 사용
}

➡️ Villager는 특별한 공격이 없으니 virtual 덕분에 기본 공격을 그대로 써도 괜찮음
➡️ 유연함이 중요할 때 → virtual


2. abstract 예시

abstract class Character
{
    public abstract void Attack();  // 반드시 구현!
}

class Knight : Character
{
    public override void Attack()
    {
        Console.WriteLine("검으로 공격!");
    }
}

class Villager : Character
{
    // ❌ Attack() 안 만들면 컴파일 에러 → 강제
}

➡️ 모든 캐릭터는 반드시 자신만의 공격을 가져야 해! → 이런 명확한 룰을 강제할 때 abstract
➡️ 설계 실수 방지하고 싶을 때 → abstract


📌 그럼 뭐가 더 효율적인가?

👉 "효율"은 상황에 따라 달라요.

기본 동작은 있지만, 바꿀 수 있도록 열어두고 싶다 ✅ virtual
꼭 자식이 직접 구현해야만 의미가 있다 ✅ abstract
공통 로직은 부모에서 쓰고, 일부만 다르게 처리하고 싶다 ✅ 템플릿 메서드 + abstract + virtual 혼용
 

🧠 결론

  • virtual → "기본은 줄게, 필요하면 바꿔 써"
    → 재사용 중심, 유연한 구조
  • abstract → "이건 네가 반드시 해"
    → 계약 기반 설계, 실수 방지, 역할 명확화

🎯 기억하기 쉽게 요약하면:

"바꿔도 되고 안 바꿔도 되면 virtual,
무조건 바꿔야 한다면 abstract"

 

음...정확하게 뭔가 와닿지는 않지만 캐릭터의 스킬이나 공격스킬로 따지자면

virtual - 기본 공격 스타일 메이플의 기본공격정도이지 않을까 싶다. 기본적으로 찌르기,내려치기,올려치기 등등 기술을 사용할때 정도?

abstract - 메이플에서의 전사,궁수,해적,마법사,도적 등등 전직했을때의 각각의 스킬로 사용할때정도로 정의되지 않을까 싶다!!  

 

🔥 비유 리뷰

virtual 기본 공격 스타일 (찌르기, 내려치기 등) "기본은 이거야, 하지만 바꿔도 돼" → 재정의 가능, 선택적
abstract 전직 클래스별 고유 스킬 (전사, 궁수, 해적 등) "반드시 직접 구현해야 해" → 자식 클래스가 구현 강제됨
 

→ 이 비유는 진짜로 설계 철학에 딱 맞아.

라고 GPT가 친절하게 설명해주네여ㅎㅎ

반응형
반응형

오늘 하루 계획

  • 오전
    • 09:00 ~ 13:00 : CS 공부
      • 어제 내용 복습 - GC, 해시테이블, 딕셔너리, .NET 런타임, 캡슐화, 박싱/언박싱 & 형변환
      • HashTable
      • Dictionary
      • HashSet
      • 제네릭 (Generics)
      • ScriptableObject
  • 오후
    • 13:00 ~ 14:00 : 점심시간
    • 14:00 ~ 18:00 : 팀 프로젝트 진행 (12조)
      • 폭탄 스킬 기능 구현 및 사운드 연결
      • 카드 수량에 따라 3x3 / 4x4 자동 배치 처리
      • 게임오버 사운드 추가
      • GameManager, Card, Board 등 스크립트 리팩토링
      • ScriptableObject Stage 데이터 기반 동작 적용 완료
    • 18:00 ~ 19:00 : 저녁시간
  • 저녁
    • 19:00 ~ 20:00 : 팀 회의
    • 20:00 ~ 20:30 : 개인 TIL 작성
    • 20:30 ~ 21:00 : 팀원 회고

1. 오늘 학습 키워드

  • HashTable
  • Dictionary
  • HashSet
  • 제네릭 (Generics)
  • ScriptableObject
  • 카드 자동 배치 (3x3, 4x4)
  • 폭탄 스킬
  • 사운드 연결
  • GameManager 구조 개선

2. 오늘 학습 한 내용을 나만의 언어로 정리하기

https://dev-jen.tistory.com/entry/%ED%95%B4%EC%8B%9C%ED%85%8C%EC%9D%B4%EB%B8%94HashTable-vs-%EB%94%95%EC%85%94%EB%84%88%EB%A6%ACDictionary%EC%A0%9C%EB%84%A4%EB%A6%ADGeneric

 

C# - 해시테이블(HashTable) vs 딕셔너리(Dictionary)&제네릭(Generic)

✅ 해시테이블 (Hash Table)📌 개념키(key)를 해시 함수(Hash Function)에 넣어 나온 해시값을 기반으로 데이터를 저장하는 자료구조.내부적으로는 배열(array)와 연결 리스트 또는 트리 구조를 함께 사용

dev-jen.tistory.com

https://dev-jen.tistory.com/entry/Unity-%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%84%B0%EB%B8%94%EC%98%A4%EB%B8%8C%EC%A0%9D%ED%8A%B8ScriptableObject%EC%98%88%EC%A0%9C

 

Unity - 스크립터블오브젝트(ScriptableObject)&예제

✅ ScriptableObject란?📌 개념 정리ScriptableObject는 MonoBehaviour처럼 Unity에서 사용하는 클래스지만,씬에 붙는 컴포넌트가 아니고, 독립적인 데이터 객체야.데이터를 자산(Asset)으로 만들어 저장하고 공

dev-jen.tistory.com

https://dev-jen.tistory.com/entry/C-%ED%95%B4%EC%89%AC%EC%85%8BHashSet-vs-DictionaryHashTable

 

C# - 해쉬셋(HashSet) vs Dictionary(HashTable)

✅ HashSet이란?중복을 허용하지 않는 데이터 집합 (Set)을 구현한 자료구조내부적으로는 **해시 테이블(Hash Table)**을 사용하지만,핵심 목적은 **"중복 없는 값의 모음"**을 만드는 거야.🔍 HashSet의

dev-jen.tistory.com

https://dev-jen.tistory.com/entry/C-%EC%9E%90%EB%A3%8C%EA%B5%AC%EC%A1%B0

 

C# - 자료구조

✅ 자료구조란?자료(데이터)를 효율적으로 저장하고, 관리하고, 사용할 수 있도록 구성한 방식쉽게 말하면,"데이터를 어떻게 정리해서 넣고, 꺼내고, 수정할지에 대한 규칙과 구조"✅ 왜 중요한

dev-jen.tistory.com

 

오늘은 자료구조의 기초부터 Unity의 실전 기능까지 폭넓게 다뤘다.

먼저 CS 공부 시간에는 해시 기반 자료구조들인 HashTable, Dictionary, HashSet의 차이를 살펴보았고,

C# 제네릭(Generic)의 개념과 장점을 학습하면서 타입 안정성과 재사용성의 중요성을 이해했다.

Unity에서는 ScriptableObject를 활용해 레벨(Stage) 정보를 분리하고, ScriptableObject로 예제까지 만들어보았다.

이를 바탕으로 카드 수량에 따라 3x3 또는 4x4로 자동 배치되도록 구현했다.

또한 폭탄 아이템 기능을 추가하고, 터질 때 사운드까지 연동했다.

게임오버 시 효과음도 추가하였고, 전반적인 스크립트 구조 (GameManager, Card, Board)를 정리했다.


3. 학습하며 겪었던 문제점 & 에러

1. ScriptableObject에서 배열 값이 null로 나오는 문제

  • 문제정의: Stage ScriptableObject에서 cardCount 배열이 정상적으로 세팅돼 있음에도 불구하고 Board.cs에서 NullReferenceException 발생.
  • 시도한 방법: StageManager가 정상적으로 초기화됐는지, ScriptableObject가 Inspector에 잘 연결되어 있는지 확인.
  • 해결 방법: StageManager.Instance.GetCurrentStage()를 사용하는 시점에서 Awake()보다 Start() 호출이 늦게 되어 참조가 실패하고 있었음. Stage 데이터를 직접 연결하거나 Board에서도 Start()에서 안전하게 참조.
  • 새롭게 알게 된 점: ScriptableObject는 인스턴스화되어 있어도 호출 시점에 따라 null 오류가 발생할 수 있음.
  • 다시 만나게 된다면: null 체크를 철저히 하고, ScriptableObject 데이터 접근은 Awake()보다 늦은 Start()에서 처리.

2. 카드 배치가 4x4가 아닌 3xN처럼 나오는 문제

  • 문제정의: 카드 수량이 16개임에도 Board.cs에서 카드가 가로 3개로만 배치되는 현상.
  • 시도한 방법: (i % 4) 및 (i / 4) 수식을 확인했으나 문제없었음.
  • 해결 방법: ScriptableObject 에서 받아오는 if문의 Level값을 내가 잘못 설정했음..
  • 새롭게 알게 된 점: 숫자를 잘 보자..
  • 다시 만나게 된다면: 하나하나 천천히 다시 체크하는 습관을 들이자.

3. 폭탄 사운드가 들리지 않거나 Destroy 오류 발생

  • 문제정의: 폭탄 프리팹 생성 시 사운드가 출력되지 않거나 "Destroying assets is not permitted" 오류 발생.
  • 시도한 방법: Instantiate(bombPrefab) 후 Destroy(bombPrefab, 2f) 사용.
  • 해결 방법: bombPrefab은 에셋이고 인스턴스가 아니므로 Destroy() 대상이 잘못됨. GameObject go = Instantiate(bombPrefab); 후 Destroy(go, 2f);로 변경하여 해결.
  • 새롭게 알게 된 점: Instantiate() 후 Destroy()는 반드시 인스턴스를 대상으로 해야 한다. 에셋 리소스를 직접 파괴하려 하면 Unity가 경고를 출력함.
  • 다시 만나게 된다면: 항상 Instantiate() 결과를 변수로 받아 Destroy()에 넘기기.

4. 메모

ScriptableObject 간단하게 구현 Gif

 

반응형

C# - 자료구조

Dev_Jen
|2025. 7. 2. 12:13
반응형

✅ 자료구조란?

자료(데이터)를 효율적으로 저장하고, 관리하고, 사용할 수 있도록 구성한 방식

쉽게 말하면,

"데이터를 어떻게 정리해서 넣고, 꺼내고, 수정할지에 대한 규칙과 구조"


✅ 왜 중요한가?

  • 빠르게 찾기 위해
  • 효율적으로 저장하기 위해
  • 복잡한 데이터를 쉽게 다루기 위해
  • 성능 좋은 알고리즘을 만들기 위해

✅ 비유로 설명하면

  • 자료구조 = 서랍장
    예를 들어, 옷을 아무데나 넣으면 나중에 찾기 힘들지?
    자료구조는 "양말은 맨 위 칸", "바지는 아래 칸"처럼 정리 기준을 정해주는 거야.

✅ 주요 자료구조 종류 (기초)

자료구조설명예시
배열 (Array) 순서대로 데이터를 저장 리스트, 숫자 모음
리스트 (List) 동적으로 크기가 변하는 배열 C#의 List<T>
스택 (Stack) 나중에 넣은 게 먼저 나옴 (LIFO) 웹 브라우저 뒤로가기
큐 (Queue) 먼저 넣은 게 먼저 나옴 (FIFO) 줄서기, 작업 대기열
해시테이블 (Hash Table) 키로 값 빠르게 찾기 전화번호부
트리 (Tree) 계층적 데이터 구조 폴더 구조, 게임 스킬트리
그래프 (Graph) 노드 간 연결 표현 길찾기, SNS 관계망
집합 (Set) 중복 없는 데이터 모음 HashSet
 

✅ 프로그래머 입장에서 왜 중요할까?

  • 자료구조에 따라 코드 성능이 완전히 달라짐
  • 빠르게 검색, 정렬, 삽입하려면 적절한 자료구조 선택이 중요
  • 코딩테스트, 실무 모두 자료구조가 기초체력임

✅ 결론

자료구조는 "데이터를 효율적으로 다루기 위한 도구상자"
잘 쓰면 빠르고, 못 쓰면 느리고 버그 많아져.

 

흠..트리랑 그래프빼고는 다 아는데 트리랑 그래프에 대해서도 공부해봐야겠다!

반응형
반응형

✅ HashSet이란?

중복을 허용하지 않는 데이터 집합 (Set)을 구현한 자료구조
내부적으로는 **해시 테이블(Hash Table)**을 사용하지만,
핵심 목적은 **"중복 없는 값의 모음"**을 만드는 거야.


🔍 HashSet의 특징

❌ 중복 허용 안 함 같은 값을 두 번 넣어도 한 번만 저장됨
🔍 순서 없음 넣은 순서 보장 안 됨
⚡ 매우 빠름 추가, 삭제, 탐색 전부 평균 O(1)
📦 내부 구조 키만 있고, 값은 없음 (딕셔너리는 key-value, 얘는 key만 존재)

✅ HashSet 예제 (C#)

using System.Collections.Generic;

HashSet<string> names = new HashSet<string>();

names.Add("Alice");
names.Add("Bob");
names.Add("Alice");  // 중복! 무시됨

Console.WriteLine(names.Count); // 출력: 2
Console.WriteLine(names.Contains("Bob")); // true

✅ HashSet vs Dictionary (or Hashtable)

키와 값 키만 존재 키와 값 존재
목적 중복 없는 목록 키로 값을 매핑
내부 구현 Hash Table 기반 Hash Table 기반
사용 예 방문 여부 확인, 유니크 목록 설정값 저장, 데이터 검색

✅ 언제 쓰나?

중복 제거 중복 있는 리스트에서 유니크한 값만 뽑을 때
빠른 포함 여부 확인 어떤 값이 존재하는지 빠르게 검사할 때
태그/이름/고유값 저장 "이미 처리했는가?"를 추적할 때

✅ 예: 방문한 적 있는 위치 추적

HashSet<Vector2Int> visited = new HashSet<Vector2Int>();

Vector2Int current = new Vector2Int(2, 3);

if (!visited.Contains(current))
{
    visited.Add(current);
    // 처음 방문한 위치 처리
}

📌 핵심 요약

HashSet<T>는 딕셔너리에서 값(value)을 없앤 버전이라고 생각하면 쉬워.
딕셔너리: key → value
해시셋: key만 저장 (중복 없음)

 

오 해쉬셋과 딕셔너리가 둘다 해시테이블 기반으로 만들어졌다는게 좀 신기했다. 딕셔너리는 해시테이블의 상위호환이지 해시테이블 기반인줄은 몰랐다. 다시 GPT에게 물어보니 해시테이블 기반이 맞다고 한다.

또 궁금한게 생겼다. 딕셔너리에서는 값을 없앤 버전이라고 언급하는데 그럼 딕셔너리는 중복이 허용 가능한가? 싶었다 다시 GPT에게 물어보니

 

딕셔너리(Dictionary)는 키(key) 중복을 허용하지 않아.
✅ 단, 값(value) 은 중복될 수 있어.


✅ 딕셔너리에서 중복이 되는 부분과 안 되는 부분

항목중복 가능 여부설명
Key ❌ 불가능 같은 키로 두 번 Add() 하면 예외 발생
Value ✅ 가능 여러 키가 같은 값을 가질 수 있음

✅ 예시로 확인해보자

 
Dictionary<string, int> dict = new Dictionary<string, int>();

dict.Add("apple", 1);
dict.Add("banana", 1);   // value는 중복 가능
dict.Add("apple", 2);    // ❌ 오류! 키 중복
System.ArgumentException: 항목이 이미 추가되었습니다.

 

즉,

  • "apple"이라는 키가 두 번 들어가는 건 안됨
  • 값인 1이 두 번 들어가는 건 가능함

✅ 해시셋은?

  • 아예 Key = Value인 구조야.
  • 따라서 모든 값 자체가 고유해야 함
  • 즉, "apple"을 두 번 넣으면 무시됨.

🧠 핵심 요약

자료구조중복 가능 여부
Dictionary ❌ 키는 중복 불가 / ✅ 값은 중복 가능
HashSet ❌ 값 중복 불가 (값 = 키)

음..이렇게 된다면 해시테이블도 궁금해졌다.

✅ Hashtable도 Dictionary와 마찬가지로

항목중복 가능 여부
Key ❌ 중복 불가
Value ✅ 중복 가능

 

확실히 딕셔너리도 해시테이블 기반으로 만들어진거다 보니 키값은 둘다 똑같이 중복이 안된다고한다.

정리하자면

HashSet - 키값만 있음, 키값 중복안됨. 약간 데이터 저장용도일까...정보를 넣었는지 안넣었는지 구별할때나 있는지 없는지 구별할떄 많이 사용할 듯 하다.

Dictionary - 키값, 값 둘다있음. 하지만 키값은 중복안됨.

HashTable - 키값, 값 둘다음. 하지만 키값은 중복안됨.

여기서봤을떄는 딕셔너리와 해시테이블이 같아 보이지만. 명확하게 차이가 나는걸 알고있다.

이렇게 계속 비교하면서 공부하는게 참 재밌는거같다

https://dev-jen.tistory.com/entry/%ED%95%B4%EC%8B%9C%ED%85%8C%EC%9D%B4%EB%B8%94HashTable-vs-%EB%94%95%EC%85%94%EB%84%88%EB%A6%ACDictionary%EC%A0%9C%EB%84%A4%EB%A6%ADGeneric

 

C# - 해시테이블(HashTable) vs 딕셔너리(Dictionary)&제네릭(Generic)

✅ 해시테이블 (Hash Table)📌 개념키(key)를 해시 함수(Hash Function)에 넣어 나온 해시값을 기반으로 데이터를 저장하는 자료구조.내부적으로는 배열(array)와 연결 리스트 또는 트리 구조를 함께 사용

dev-jen.tistory.com

 

반응형

'C#' 카테고리의 다른 글

C# - Virtual & abstract  (0) 2025.07.03
C# - 자료구조  (1) 2025.07.02
C# - 해시테이블(HashTable) vs 딕셔너리(Dictionary)&제네릭(Generic)  (0) 2025.07.02
C# - .NET Runtime  (1) 2025.07.01
C# - 캡슐화  (1) 2025.07.01
반응형

✅ ScriptableObject란?

📌 개념 정리

  • ScriptableObject는 MonoBehaviour처럼 Unity에서 사용하는 클래스지만,
  • 씬에 붙는 컴포넌트가 아니고, 독립적인 데이터 객체야.
  • 데이터를 자산(Asset)으로 만들어 저장하고 공유할 수 있는 방식을 제공해.

✅ 언제 사용하나요?

여러 객체가 동일한 데이터를 참조할 때 예: 스탯, 설정값, 아이템 정보 등
프리팹이나 코드와 독립적으로 데이터 관리하고 싶을 때 설정 변경이 쉽고 유지보수 편함
인스펙터에서 직접 데이터 입력하고 저장하고 싶을 때 커스텀 에디터 연동도 쉬움

✅ 간단한 예제: 캐릭터 능력치 데이터 만들기

1. CharacterStats.cs

using UnityEngine;

[CreateAssetMenu(fileName = "NewCharacterStats", menuName = "ScriptableObjects/CharacterStats")]
public class CharacterStats : ScriptableObject
{
    public string characterName;
    public int maxHP;
    public int attackPower;
    public float moveSpeed;
}

2. 생성 방법

  • Unity 에디터에서 우클릭 → Create → ScriptableObjects → CharacterStats
  • 이름을 WarriorStats, MageStats 등으로 지정하고 값 입력

3. 사용하는 스크립트

public class Player : MonoBehaviour
{
    public CharacterStats stats;

    private void Start()
    {
        Debug.Log($"{stats.characterName}의 공격력: {stats.attackPower}");
    }
}
  • Player 오브젝트에 붙이고, stats 필드에 ScriptableObject 자산을 할당하면 끝!

✅ ScriptableObject vs MonoBehaviour

씬에 존재 ❌ (씬에 안 붙음) ✅ (오브젝트에 붙임)
메모리 효율 ✅ 매우 좋음 ❌ 반복 사용 시 비효율
역할 데이터 저장, 설정 동작 로직, 이벤트 처리
재사용 여러 오브젝트가 참조 가능 오브젝트마다 개별

 


✅ 장점 정리

  • ✔️ 씬 독립적 데이터 관리 가능 (게임 밸런스 조정 시 유리)
  • ✔️ 프리팹 재사용 시 같은 데이터를 공유할 수 있어 메모리 절약
  • ✔️ 버전 관리에 유리 (데이터가 파일로 존재하니까 Git에도 딱!)
  • ✔️ 유닛 테스트에 적합

✅ 단점 or 주의사항

  • ScriptableObject는 **상태(state)**를 저장하기보단 설정값(config) 저장에 적합함
  • 런타임에 값을 바꾸면 에디터에 저장된 자산이 수정될 수도 있으므로 주의

✅ 실생활 비유

  • MonoBehaviour는 "게임에 나오는 배우(오브젝트)"
  • ScriptableObject는 "그 배우가 참고하는 대본(데이터)"

으음 데이터로 활용하는것은 너무 좋아보인다 처음 이걸 봤을때 왜 이걸 몰랐을까? 싶었다 엄청 유용하게 많이 사용될듯하다 보통 인벤토리 시스템에 많이 사용하는거 같다 스킬/아이템 데이터등등..

간단하게 유니티로 뭐라도 만들어 보자!

 

대충 요약하자면

📦 전체 구성 요약

1. ScriptableObject 기반 아이템 데이터 관리

  • Item_ScriptableObject 클래스 생성
  • 인스펙터에서 여러 개의 아이템 데이터를 .asset 파일로 생성 (ItemData1~4)
  • 각 아이템은 고유 번호, 이름, 설명, 파워 등의 정보를 가짐

2. 아이템 오브젝트 (Item)

  • ItemObject 스크립트 부착
  • 각각의 오브젝트가 하나의 ScriptableObject 참조
  • Tag는 "Item"으로 지정

3. 플레이어

  • WASD 키 입력으로 2D 이동
  • Rigidbody2D + Collider2D 구성
  • Item과 충돌 시 해당 데이터 획득

4. UI 시스템

  • ItemUIManager 스크립트로 Text 컴포넌트 제어
  • 아이템 정보 표시 및 초기화 담당

흠..근데 사실 고유 번호는 필요 없었을거같다. 이런식으로 아이템 정보를 저장해서 사용할수있는걸 알게됐다!

 

나머지 코드

Item_ScriptableObject.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

[CreateAssetMenu(fileName ="ItemData", menuName = "ScriptableObject/ItemData")]
public class Item_ScriptableObject : ScriptableObject
{
    public int itemNum;
    public string itemName;
    public string iteminfo;
    public int itemPower;
}

ItemObject.cs - 아이템에 들어가는 스크립트 - Tag Item으로 추가해줘야한다.

using UnityEngine;

public class ItemObject : MonoBehaviour
{
    public Item_ScriptableObject itemData;
}

ItemUIManager.cs 이다 여기서 아이템의 설명과 정보를 출력

using UnityEngine;
using UnityEngine.UI;

public class ItemUIManager : MonoBehaviour
{
    public Text nameText;
    public Text infoText;
    public Text powerText;
    public GameObject panel;

    private void Start()
    {
        panel.SetActive(false);
    }

    public void ShowItemInfo(Item_ScriptableObject item)
    {
        panel.SetActive(true);
        nameText.text = $"이름: {item.itemName}";
        infoText.text = $"설명: {item.iteminfo}";
        powerText.text = $"파워: {item.itemPower}";
    }

    public void HideInfo()
    {
        panel.SetActive(false);
        nameText.text = "";
        infoText.text = "";
        powerText.text = "";
    }
}

Player.cs 이다. Item과 닿는다면 UI매니저를 이용해서 아이템의 정보를 받아온다.

using UnityEngine;

public class Player : MonoBehaviour
{
    public ItemUIManager uiManager;

    private void OnTriggerEnter2D(Collider2D other)
    {
        if (other.CompareTag("Item"))
        {
            ItemObject itemObj = other.GetComponent<ItemObject>();
            if (itemObj != null && itemObj.itemData != null)
            {
                uiManager.ShowItemInfo(itemObj.itemData);
            }
        }
    }

    private void OnTriggerExit2D(Collider2D other)
    {
        if (other.CompareTag("Item"))
        {
            uiManager.HideInfo();
        }
    }
}

 

나머지 코드 1개는 PlayerMovement인데 간단하니까 넘어가겠다.

반응형
반응형

✅ 해시테이블 (Hash Table)

📌 개념

  • 키(key)를 해시 함수(Hash Function)에 넣어 나온 해시값을 기반으로 데이터를 저장하는 자료구조.
  • 내부적으로는 배열(array)와 연결 리스트 또는 트리 구조를 함께 사용해 충돌을 해결함.

📌 동작 방식

  1. Key → 해시 함수 → 인덱스 계산
  2. 계산된 인덱스에 값을 저장
  3. 같은 인덱스에 이미 값이 있으면 충돌(Collision) 발생
  4. 충돌 해결법: 체이닝(Chaining), 오픈 어드레싱(Open Addressing)

📌 시간 복잡도 (평균적 상황 기준)

  • 삽입: O(1)
  • 삭제: O(1)
  • 탐색: O(1)

※ 최악의 경우에는 O(n) (모든 키가 같은 인덱스로 해시됨)


✅ 딕셔너리 (Dictionary in C#)

📌 개념

  • C#에서의 Dictionary<TKey, TValue>는 해시테이블 기반으로 구현된 제네릭 키-값(key-value) 쌍 자료구조.
  • 즉, 해시테이블을 제네릭 버전으로 구현한 클래스라고 보면 됨.

📌 사용 예시

Dictionary<string, int> ages = new Dictionary<string, int>();
ages["Alice"] = 25;
ages["Bob"] = 30;

Console.WriteLine(ages["Alice"]);  // 25

📌 주요 메서드

  • Add(key, value) : 새 항목 추가
  • Remove(key) : 항목 제거
  • ContainsKey(key) : 특정 키 존재 여부 확인
  • TryGetValue(key, out value) : 키가 있으면 값을 out 파라미터로 반환

🔍 해시테이블 vs 딕셔너리 차이점

제네릭 지원 ❌ (object 기반)
타입 안정성 낮음 (박싱/언박싱 필요) 높음
성능 상대적으로 느릴 수 있음 더 빠름
쓰레드 안전 기본적으로 안전하지 않음 마찬가지 (별도 처리 필요)
사용 권장 구형 코드 유지 보수시 신규 개발시 권장

 

사실 해시테이블의 상위호환이 딕셔너리 라고 한다. 해시테이블은 사용할떄마다 박싱/언박싱을 하기 때문에 너무 비효율 적이다. 쓰레드까지 들어간다면 솔직히 잘 모르겠다.. 어떻게 안전하지 않다는 기준은 뭔진 모르겠다. 

그리고 제네릭 지원이라는 것이 궁금했다. 제너릭이란건 자세히 뭘까

 

✅ 제네릭이란?

  • 하나의 클래스나 메서드가 다양한 데이터 타입을 처리할 수 있도록 해주는 기능
  • 코드를 타입에 의존하지 않고 작성하고, 나중에 사용할 때 구체적인 타입을 지정
  • 성능이 좋고 타입 안정성도 보장됨 (컴파일 타임에 타입 검사)

🔍 예시 (제네릭 없이 작성한 코드)

ArrayList list = new ArrayList();
list.Add(1);          // int
list.Add("hello");    // string

int num = (int)list[0];  // 형변환 필요 (박싱/언박싱)
  • 타입이 object이기 때문에 어떤 값이든 넣을 수 있지만,
  • 형변환이 필요하고, 런타임 오류가 발생할 수 있음

✅ 제네릭 사용 예시

List<int> intList = new List<int>();
intList.Add(1);
intList.Add(2);

int num = intList[0]; // 형변환 필요 없음!
  • List<int>는 int형만 저장 가능
  • 컴파일 타임에 타입 검사 가능 → 오류 예방
  • 형변환이 없어져서 성능도 좋아짐

🔧 직접 만든 제네릭 클래스 예시

 
public class Box<T>  // T는 타입 매개변수 (Type Parameter)
{
    public T item;

    public void SetItem(T value)
    {
        item = value;
    }

    public T GetItem()
    {
        return item;
    }
}

💡 왜 제네릭을 사용할까?

이유설명
코드 재사용성 하나의 코드로 다양한 타입 처리 가능
타입 안정성 컴파일 타임에 타입 검사 가능
성능 향상 박싱/언박싱 제거로 메모리/속도 효율적

흠..GPT가 BOX<T>라는 예시를 줬는데 이 <T>가 뭔지 정확히 모르겠다 잘 사용하지 않기도 했고. 그래서 물어봤다.

 

✅ Box<T>란?

📦 정의

  • Box<T>는 "어떤 타입 T의 데이터를 담을 수 있는 상자"를 의미하는 클래스야.
  • 여기서 T는 타입 매개변수(Type Parameter) 로, 사용할 때 구체적인 타입으로 대체돼.
public class Box<T>
{
    public T item;

    public void SetItem(T value)
    {
        item = value;
    }

    public T GetItem()
    {
        return item;
    }
}

✅ 핵심 개념

용어설명
T 타입 매개변수. 보통 T(Type), K(Key), V(Value) 같은 문자로 씀
Box<T> T라는 타입을 나중에 지정할 수 있는 클래스
재사용성 Box<int>, Box<string> 등으로 여러 타입에 재사용 가능
 

✅ 실제 사용 예

Box<int> intBox = new Box<int>();
intBox.SetItem(10);                  // int 타입 사용
Console.WriteLine(intBox.GetItem());  // 출력: 10

Box<string> strBox = new Box<string>();
strBox.SetItem("hello");            // string 타입 사용
Console.WriteLine(strBox.GetItem());  // 출력: hello
  • Box<int>라고 선언하면 T는 int로 바뀌고,
  • Box<string>이라고 하면 T는 string으로 바뀌는 거야.

✅ 왜 이렇게 쓰는 걸까?

  1. 타입마다 클래스를 일일이 만들 필요 없음
  2. 형변환 없이 안전하게 값 사용 가능
  3. 컴파일 시 타입 검사 가능 → 오류 예방

✅ Box<T>를 실생활에 비유하면?

  • Box<T>는 "어떤 물건이든 담을 수 있는 상자"
  • 사용할 때 "이 상자에는 **책(int)**만 담을 거야", 혹은 "**편지(string)**만 담을 거야" 하고 말해주는 느낌이야.

흠..일단 그럼 T는 다양한 형식을 받을 수 있지만 그 중에 1종류만 받는다는 느낌이 강하다 생각했다 그러면 <T>라면 정말 어떤 타입이 올지 몰라서 대기하는 느낌이고 평소에 게임을 만든다면 <int>등등 정확한 타입을 써서 정의내리는게 더 깔끔할듯하다. 

✅ 왜 Box<int> 같은 걸 쓸까?

1. 타입별 클래스를 중복 작성할 필요 없게 해줌

❌ 제네릭 없이 하면:

public class IntBox {
    public int item;
}

public class StringBox {
    public string item;
}
  • int, string, float… 다 다른 클래스를 만들어야 해.
  • 유지보수 힘듦 + 코드 중복

✅ 제네릭을 쓰면:

public class Box<T> {
    public T item;
}
  • 한 번만 만들면 어떤 타입이든 다 쓸 수 있어: Box<int>, Box<string>, Box<float>...

2. 타입 안정성과 성능을 동시에 보장

❌ object 기반이면:

 
public class ObjectBox {
    public object item;
}
ObjectBox box = new ObjectBox();
box.item = 10;

int value = (int)box.item; // 형변환 필요 → 실수로 string을 넣었다면? → 런타임 오류
  • object는 어떤 타입이든 담을 수 있지만 형변환 필요하고, 런타임 오류 가능성 있음

✅ 제네릭 쓰면:

Box<int> box = new Box<int>();
box.item = 10;

int value = box.item; // 형변환 없음! 타입 안전!
  • 컴파일 타임에 타입 검사
  • 형변환 없어져서 성능도 더 좋음

3. 자료구조를 만들 때 필수

예를 들어, 우리가 직접 Stack, Queue, List 같은 자료구조를 만든다고 해보자.

public class MyStack<T> {
    private List<T> data = new List<T>();

    public void Push(T item) {
        data.Add(item);
    }

    public T Pop() {
        T item = data[data.Count - 1];
        data.RemoveAt(data.Count - 1);
        return item;
    }
}

📌 결론

Box<int>는 그냥 "정수를 담는 상자"고, 제네릭을 사용함으로써 우리는

  • 중복 코드 없이
  • 타입 안정성 확보하면서
  • 다양한 타입에 대해 유연하게 대응 가능한 클래스를 만들 수 있어.

 

음.. 그렇다고한다 확실히 제네릭을 쓰는게 훨씬 효율적이고 형변환도 필요없으니 좋은듯하다.

반응형

'C#' 카테고리의 다른 글

C# - 자료구조  (1) 2025.07.02
C# - 해쉬셋(HashSet) vs Dictionary(HashTable)  (2) 2025.07.02
C# - .NET Runtime  (1) 2025.07.01
C# - 캡슐화  (1) 2025.07.01
C# - 박싱/언박싱과 박싱/언박싱&형변환 차이  (0) 2025.07.01
반응형

1. 오늘 학습 키워드

Unity, Card 시스템, 애니메이션 제한, 클릭 제한, Git 협업, Detached HEAD, ScriptableObject 개념, Update 주기, GC, 캡슐화


2. 오늘 학습 한 내용을 나만의 언어로 정리하기

 

오전에는 컴퓨터 사이언스 관련 기초 개념을 정리했다.

  • Unity에서의 Update, FixedUpdate, LateUpdate 주기 차이를 이해하고
  • 객체지향 원칙 중 하나인 **캡슐화(Encapsulation)**에 대해 복습했다.
  • C#에서 **GC(Garbage Collector)**의 동작 조건과 실행 시점도 정리했으며,
  • 값 형식과 참조 형식의 차이를 통해 메모리 구조와 호출 방식을 구분했다.

또한 C#의 박싱(Boxing)과 언박싱(Unboxing) 개념을 실습하며 성능 이슈를 확인했고,
컬렉션 구조인 Hashtable과 Dictionary의 차이점, 제네릭 사용 여부, 검색 속도 특성 등도 비교하며 학습했다.

 

오후는 Unity 팀 프로젝트에서 카드 시스템을 다듬는 데 집중했다.

카드가 애니메이션 중일 때 다른 카드가 클릭되지 않도록 static bool canClick을 사용해 클릭 제한 로직을 구현했고, 카드 2장까지만 열리고 3번째 카드를 클릭하면 기존 2장을 자동으로 닫는 방식으로 OpenCard() 로직도 수정했다.

GitHub Desktop에서 Detached HEAD 상태에 빠졌을 때 브랜치로 다시 Checkout하는 방법도 익혔고, Publish branch 버튼이 비활성화되는 이유와 해결 방법도 이해하게 되었다.

중간에 폭탄 카드(idx == -1) 처리를 시도했지만, Sprite 대신 프리팹이 들어있는 구조라서 일단 내일 다시 시도하기로 했다.

또한 ScriptableObject가 뭔지, 어떤 구조로 활용되는지 예제를 통해 개념적으로 정리했다.

 

팀 프로젝트

  • 2장씩 넘어가게 (나머지는 닫힘) (완료)
  • 카드 애니메이션 제작 (완료)
  • 카드 기본 이미지 꾸미기 (완료)
  • 성공했을떄 UI Particle 추가 (완료)

3. 학습하며 겪었던 문제점 & 에러

🔸 정의

  • 카드 애니메이션 중 다른 카드가 눌리는 문제
  • GitHub에서 Publish 버튼이 비활성화되고 Detached HEAD 상태가 나타남

🔸 시도

  • canClick bool 변수를 통해 클릭 제한 제어
  • Invoke()로 애니메이션 후 다시 클릭 가능하게 함
  • GitHub 브랜치 상태 및 Checkout 관련 메뉴 확인

🔸 해결 방법

  • OpenCard()에 canClick 조건 추가
  • Invoke("EnableClick", 0.6f) 방식으로 타이밍 제어
  • 브랜치 복귀하여 정상적인 상태로 되돌림

🔸 새롭게 알게 된 점

  • Detached HEAD는 브랜치가 아닌 커밋만 보고 있는 상태이며, 이 상태에선 커밋을 푸시할 수 없음
  • ScriptableObject는 게임 데이터 저장 및 재사용에 유용한 구조이며, 코드와 데이터를 분리할 수 있음

🔸 다시 만나게 된다면?

  • 애니메이션이 끝나는 정확한 시점을 Animation Event로 관리하면 더 안정적일 것
  • Git 상태는 항상 브랜치 이름과 History를 먼저 확인하고 작업할 것

4. 내일 학습 할 것

CS 지식, 팀 프로젝트 나머지 보완 문제


5. 메모

스택 - 정적으로 메모리에 할당. (후입선출, LIFO)

힙 - 동적으로 메모리에 할당. (선입선출, FIFO) (GC가 이 영역의 사용되지 않는 데이터들을 소멸시킴 - 언제?)

value type(값 형식) - 데이터를 메모리의 스택영역에 할당. 스택 메모리 공간에 (값)을 가지고 있다.

reference type(참조 형식) - 데이터를 힙영역에 할당. 스택 메모리 공간에(주소)를 가지고 있다. (그럼 데이터는 힙에있고 스택에는 주소값만?)

박싱 - 값 형식을 참조 형식으로 변환하는 것. 스택에 있는 값을 복사하여 힙에 할당한다.

언박싱 - 박싱했던 값을 다시 원상태로 복귀 시키는 것. 힙에 있던 데이터를 스택으로 복사한다. 힙에 가비지가 쌓이게 된다.(언제?)

해시테이블도 키값으로 값을 찾음 - 딕셔너리(상위호환)

해시테이블은 사용할때 언박싱을 해줘야한다. - 오? - 딕셔너리는 필요없음

최대기반의 GC컬렉터 - 마크 1,2,3 세대가 오래될수록 --

 

팀프로젝트 중 오류 - 즐거웠다

    public void Setting(int number)
    {
        idx = number;
        frontImage.sprite = Resources.Load<Sprite>($"Team_{idx}");

        if (frontImage.sprite != null)
        {
            frontImage.transform.localScale = Vector3.one;

            float spriteWidth = frontImage.sprite.bounds.size.x;
            float spriteHeight = frontImage.sprite.bounds.size.y;

            float targetWidth = 0.8f;
            float targetHeight = 1f;

            float scaleX = targetWidth / spriteWidth;
            float scaleY = targetHeight / spriteHeight;

            frontImage.transform.localScale = new Vector3(scaleX, scaleY, 1f);
        }
    }

카드 스크립트중 셋팅값에서 카드가 자꾸 이상하게 스케일이 커지고 작아지는 문제가 있었다. 그래서 이것저것 건드려보고 애니메이션에서도 스케일값을 수정해봤는데 이상했었다. 근데이제 진원님이 만드신 CardMover라는 코드에서 카드의 초기 스케일값과 마지막 스케일값을 조정하는 변수를 발견했다!! 이걸 발견하기전에는 카드값이 제각각으로 막 커지고 작아졌었는데 이 값을 수정하니 완벽하게 됐다.

이 과정을 겪으며 카드가 이상하게 스케일값이 왔다갔다하며 화면을 팀원들과 같이 봤었는데 다들 왜이렇게 되냐며 즐거웠다ㅋㅋㅋ 이렇게 팀프로젝트를 진행하며 재밌는 경험도하고 같이 웃으며 팀 프로젝트를 진행하니 나도 즐거웠다!!

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using DG.Tweening;

public class CardMover : MonoBehaviour
{
    public Card _card = null;

    [Header("카드 이동 시간 / 카드 딜레이 시간")]
    public float moveDuration = 0.5f;           // 이동 시간
    public float delayBetween = 0.1f;           // 딜레이 시간

    [Header("이동 타입")]
    public Ease moveEase = Ease.OutBack;        // 이동 타입

    [Header("카드의 초기 크기 ~ 최종 크기")]
    public Vector3 startScale = Vector3.zero;   // 시작 스케일
    public Vector3 endScale = Vector3.one;      // 끝 스케일

    public void Show(Vector2 vec_TargetPos, Card card, int index)
    {
        if (_card != null) return;
        _card = card;

        Transform tf = _card.transform;

        // 초기화
        tf.localPosition = Vector3.zero;
        tf.localScale = startScale;

        float delay = delayBetween * index;

        // 로컬 위치 이동
        tf
          .DOLocalMove(vec_TargetPos, moveDuration)
          .SetDelay(delay)
          .SetEase(moveEase);

        // 스케일 애니메이션
        tf
          .DOScale(endScale, moveDuration)
          .SetDelay(delay)
          .SetEase(moveEase);
    }

    private void OnDisable()
    {
        _card = null;
    }
}

결정적인 CardMover의 스크립트였다.


오늘 오전에 공부한 내용

https://dev-jen.tistory.com/entry/C-Unity-%EA%B0%80%EB%B9%84%EC%A7%80-%EC%BB%AC%EB%A0%89%ED%84%B0GC

 

C# Unity - 가비지 컬렉터(GC) 자동실행기준

✅ 가비지 컬렉터(GC)의 역할이란?더 이상 사용되지 않는 힙의 메모리를 자동으로 찾아서 정리해주는 시스템즉,메모리 누수 방지수동으로 메모리 해제하지 않아도 됨 (delete, free 필요 없음)✅ GC

dev-jen.tistory.com

https://dev-jen.tistory.com/entry/C-%EB%B0%95%EC%8B%B1%EC%96%B8%EB%B0%95%EC%8B%B1%EA%B3%BC-%EB%B0%95%EC%8B%B1%EC%96%B8%EB%B0%95%EC%8B%B1%ED%98%95%EB%B3%80%ED%99%98-%EC%B0%A8%EC%9D%B4

 

C# - 박싱/언박싱과 박싱/언박싱&형변환 차이

✅ 박싱(Boxing) & 언박싱(Unboxing)🔸 박싱(Boxing)값 형식(Value Type) 데이터를 **참조 형식(Reference Type)**으로 변환하는 것값 타입(예: int, float)을 object 타입에 저장하면 박싱이 발생이 과정에서 값이 힙

dev-jen.tistory.com

https://dev-jen.tistory.com/entry/C-%EC%BA%A1%EC%8A%90%ED%99%94

 

C# - 캡슐화

✅ 캡슐화(Encapsulation)란?"객체의 내부 상태(데이터)를 외부로부터 숨기고, 허용된 방식으로만 접근하도록 제한하는 것."✅ 핵심 요약항목설명목적데이터를 보호하고, 잘못된 접근/수정을 방지수

dev-jen.tistory.com

https://dev-jen.tistory.com/entry/Unity-Update-FixedUpdate-LateUpdate

 

Unity - Update(), FixedUpdate(), LateUpdate()

✅ Unity의 3가지 주요 루프 함수함수 이름실행 주기용도특징Update()매 프레임마다사용자 입력, 일반 로직프레임 속도에 따라 실행 빈도 달라짐 (가변)FixedUpdate()고정 시간 간격마다물리 연산 (Rigidb

dev-jen.tistory.com

https://dev-jen.tistory.com/entry/C-NET-Runtime

 

C# - .NET Runtime

✅ .NET 런타임이란?.NET Runtime은 C#, F#, VB.NET 같은 **.NET 언어로 만든 프로그램을 실행해주는 "실행 환경(엔진)"**이야.다시 말해, .NET 코드가 컴퓨터에서 실제로 돌아가도록 도와주는 핵심 소프트웨

dev-jen.tistory.com

 

반응형