no image
C# - 콘솔 블랙잭 게임 & 사용되는 예제들
1. 게임 기획게임 목표 정의 (21점을 넘지 않으면서 딜러보다 높은 점수 획득)플레이어 수 (1인 vs 딜러)승패 조건 정의카드 규칙 요약 (A=1 또는 11, J/Q/K=10, 숫자카드=해당 숫자)2. 기본 구조 설계클래스 설계Card (문양, 숫자)Deck (카드 덱, 섞기, 카드 뽑기)Player (카드 리스트, 점수 계산)GameManager (게임 흐름 제어)Enum 설계Suit (Hearts, Spades, Clubs, Diamonds)Rank (Ace ~ King)3. 게임 로직 구현카드 덱 생성 및 셔플플레이어/딜러에게 카드 2장씩 배분점수 계산 로직 (A의 처리 포함)플레이어 선택 (Hit / Stand)딜러 동작 (점수 17 이상일 때까지 Hit)승패 판정 및 출력4. 입출력 처리콘솔..
2025.07.09
C#
no image
TIL - 내일배움캠프 7일차 TIL [C# 튜플과 LINQ로 배우는 컬렉션 활용 + Snake 게임 제작기]
🗓️ 오늘 하루 일정✅ 오전09:00 ~ 09:10 : 팀원들과 소통 및 자기소개09:10 ~ 11:30 : 개인 공부스네이크 게임 제작맵 구현, Snake 리스트로 표현, 키 입력 처리 구현 등11:30 ~ 13:00 : C# 체크리스트 강의 수강변수와 자료형, ConsoleKey, 콘솔 단축키 (Ctrl+Shift+F, Ctrl+R+R, Shift+F12) 등 학습🍽️ 점심시간13:00 ~ 14:00 : 점심시간✅ 오후14:00 ~ 18:00 : 개인 공부 및 Snake 게임 마무리튜플, 리스트, ConsoleKey, Thread.Sleep(), snake.Clear() 등 이해게임 로직 완성 및 클래스 다이어그램 정리스택 프레임과 함수 호출 구조 시각화 학습LINQ, Any(), 람다식 구조 심..
2025.07.08
no image
C# - C#으로 간단한 아이템 매니저 구현하기 - List와 LINQ로 컬렉션 관리 연습
public class ItemInstance{ public int id; // 유니크한 아이디 public int itemId; // 같은 종류의 아이템끼리 공유하는 ID // 기타 필요한 속성들...}public class ItemManager{ private List _items; public ItemManager(List items) { _items = items; UnityEngine.Debug.LogError(_items.Count); // 아이템 개수 로그 (유니티 콘솔) } // 1. id로 단일 아이템 찾기 public ItemInstance GetItem(int id) { ret..
2025.07.08
C#
no image
C# - Linq(Language Integrated Query)
🧠 LINQ란?LINQ는 "Language Integrated Query"의 약자로,C# 코드 안에서 SQL처럼 데이터를 쉽게 조회하고 가공할 수 있게 해주는 문법이야.✅ LINQ의 목적배열, 리스트, 딕셔너리, 데이터베이스 등 여러 종류의 컬렉션에서데이터를 필터링, 정렬, 변형, 집계할 수 있음반복문 없이 간결하고 가독성 좋은 코드로 처리 가능📌 LINQ 기본 문법 (2가지 방식)메서드 체이닝 방식 (람다식 기반)쿼리 식(Query Expression) 방식 (SQL처럼 생김)✅ 예제: 리스트에서 짝수만 고르기🔹 1. 메서드 체이닝 방식List numbers = new List { 1, 2, 3, 4, 5, 6 };var evenNumbers = numbers.Where(n => n % 2 == ..
2025.07.08
C#
no image
C# - 스택 프레임이란? (Stack Frame) vs 스택(Stack)
🧠 스택 프레임이란?함수를 호출할 때마다 생기는 임시 메모리 공간이자 작업 단위 블록각 함수마다 **자신만의 공간(프레임)**을 갖고, 거기서 매개변수, 지역 변수, 복귀 주소 등을 저장해✅ 스택 메모리와의 관계스택(Stack): LIFO 구조로, 함수가 호출될수록 위로 쌓임함수가 종료되면 스택 프레임이 제거됨→ 즉, 스택 프레임은 함수의 실행 상태를 담는 메모리 상자📦 스택 프레임 구성 요소 (간단히)매개변수 (Parameters)지역 변수 (Local variables)복귀 주소 (Return address)함수가 끝났을 때, 어디로 돌아갈지 저장이전 스택 프레임 포인터호출한 함수의 위치 기억 (연결)🔁 함수 호출 예시static void Main(){ SayHello("재은");}stat..
2025.07.08
C#
no image
C# - List(tuple) vs Dictionary
한참 리스트의 튜플로 코드로 장난치고 있었는데 그럼 딕셔너리와의 엄청난 치이점이 뭘까.. 싶었다물론 리스트와 딕셔너리도 비슷하게 사용할 수 있다.List items = new List{ (0, "나무칼"), (1, "나무도끼"), (2, "나무창"), ...};이런식으로 리스트 튜플을 만들어서 만들다가 이렇게 되면 딕셔너리와 차이점이 뭐지? 싶었다하지만 뭐 다들 아시다싶이 리스트 튜플로는 키와 값 으로 있는게 아니라 여러개를 더 넣을 수 있다.List items = new List();items.Add((0, "나무칼", "나무칼이다"));items.Add((1, "돌칼", "돌로 만든 칼이다"));items.Add((2, "철칼", "철로 만든 칼이다"));items.Add((3,..
2025.07.08
C#
no image
C# - ConsoleKey(콘솔키)
✅ ConsoleKey란?ConsoleKey는 C#에서 키보드의 실제 키를 표현하기 위한 **열거형(enum)**이야.예를 들어, 키보드의 화살표 키, Enter, A, B, 숫자 1~9, ESC 등은모두 ConsoleKey의 값으로 표현할 수 있어.✅ 사용 예시ConsoleKeyInfo keyInfo = Console.ReadKey(true);ConsoleKey key = keyInfo.Key;if (key == ConsoleKey.LeftArrow){ Console.WriteLine("왼쪽으로 이동");}Console.ReadKey() → 사용자가 누른 키를 읽음.Key → 어떤 키인지 ConsoleKey로 반환ConsoleKey.LeftArrow → 키보드 왼쪽 화살표✅ 자주 사용하는 Cons..
2025.07.08
C#
no image
C# - Tuple(튜플) vs Struct(구조체)
✅ 튜플이란?서로 다른 타입의 데이터를 한 번에 묶어서 저장할 수 있는 구조배열이나 리스트처럼 하나의 타입만 저장하는 게 아니라,여러 값을 하나의 단위로 다룰 수 있어🔸 기본 예제var person = ("Alice", 25);Console.WriteLine(person.Item1); // "Alice"Console.WriteLine(person.Item2); // 25❓여기서 나오는 Item1,2,3도 궁금해서 찾아봤다.✅ 기본 튜플 (이름 없이 생성)var tuple = ("Apple", 123, true);Console.WriteLine(tuple.Item1); // "Apple"Console.WriteLine(tuple.Item2); // 123Console.WriteLine(tuple.Item..
2025.07.08
C#
반응형

1. 게임 기획

  • 게임 목표 정의 (21점을 넘지 않으면서 딜러보다 높은 점수 획득)
  • 플레이어 수 (1인 vs 딜러)
  • 승패 조건 정의
  • 카드 규칙 요약 (A=1 또는 11, J/Q/K=10, 숫자카드=해당 숫자)

2. 기본 구조 설계

  • 클래스 설계
    • Card (문양, 숫자)
    • Deck (카드 덱, 섞기, 카드 뽑기)
    • Player (카드 리스트, 점수 계산)
    • GameManager (게임 흐름 제어)
  • Enum 설계
    • Suit (Hearts, Spades, Clubs, Diamonds)
    • Rank (Ace ~ King)

3. 게임 로직 구현

  • 카드 덱 생성 및 셔플
  • 플레이어/딜러에게 카드 2장씩 배분
  • 점수 계산 로직 (A의 처리 포함)
  • 플레이어 선택 (Hit / Stand)
  • 딜러 동작 (점수 17 이상일 때까지 Hit)
  • 승패 판정 및 출력

4. 입출력 처리

  • 콘솔 출력 (카드 정보, 점수 등)
  • 사용자 입력 처리 (Console.ReadLine())

5. 게임 반복 및 종료 처리

  • 다시 시작 여부 묻기
  • 플레이어, 덱 초기화 로직 추가
using System;
using System.Collections.Generic;

// 카드 무늬 정의
public enum Suit { Hearts, Diamonds, Clubs, Spades }

// 카드 숫자 정의 (J/Q/K는 10, Ace는 11로 처리)
public enum Rank
{
    Two = 2, Three, Four, Five, Six, Seven, Eight, Nine, Ten,
    Jack = 10, Queen = 10, King = 10, Ace = 11
}

// 개별 카드 클래스
public class Card
{
    public Suit Suit { get; }
    public Rank Rank { get; }

    public Card(Suit suit, Rank rank)
    {
        Suit = suit;
        Rank = rank;
    }

    // 카드의 점수 반환
    public int GetValue() => (int)Rank;

    public override string ToString()
    {
        return $"{Rank} of {Suit}";
    }
}

// 카드 덱 클래스
public class Deck
{
    private List<Card> cards = new List<Card>();
    private Random rng = new Random();

    public Deck()
    {
        // 모든 카드 조합 생성
        foreach (Suit suit in Enum.GetValues(typeof(Suit)))
        {
            for (int i = 2; i <= 10; i++)
                cards.Add(new Card(suit, (Rank)i));

            cards.Add(new Card(suit, Rank.Jack));
            cards.Add(new Card(suit, Rank.Queen));
            cards.Add(new Card(suit, Rank.King));
            cards.Add(new Card(suit, Rank.Ace));
        }

        Shuffle();
    }

    // 카드 섞기
    public void Shuffle()
    {
        for (int i = 0; i < cards.Count; i++)
        {
            int j = rng.Next(i, cards.Count);
            (cards[i], cards[j]) = (cards[j], cards[i]);
        }
    }

    // 카드 한 장 뽑기
    public Card DrawCard()
    {
        Card card = cards[0];
        cards.RemoveAt(0);
        return card;
    }
}

// 플레이어 클래스 (플레이어 및 딜러 공통 사용)
public class Player
{
    public string Name { get; }
    public List<Card> Hand { get; private set; } = new List<Card>();

    public Player(string name)
    {
        Name = name;
    }

    public void AddCard(Card card) => Hand.Add(card);
    public void ClearHand() => Hand.Clear();

    // 점수 계산 (Ace = 11 또는 1로 조절)
    public int GetScore()
    {
        int score = 0;
        int aceCount = 0;

        foreach (var card in Hand)
        {
            int value = card.GetValue();
            score += value;
            if (card.Rank == Rank.Ace) aceCount++;
        }

        // 점수가 21 초과면 Ace를 1로 조정
        while (score > 21 && aceCount > 0)
        {
            score -= 10;
            aceCount--;
        }

        return score;
    }

    // 플레이어의 카드 보여주기
    public void ShowHand(bool showAll = true)
    {
        Console.WriteLine($"\n[{Name}의 카드]");
        for (int i = 0; i < Hand.Count; i++)
        {
            if (!showAll && i == 0)
                Console.WriteLine("[첫 번째 카드는 비공개]");
            else
                Console.WriteLine(Hand[i]);
        }

        if (showAll)
            Console.WriteLine($"현재 점수: {GetScore()}");
    }
}

// 게임 전체를 관리하는 클래스
public class GameManager
{
    private Deck deck;
    private Player player;
    private Player dealer;

    // 게임 시작
    public void StartGame()
    {
        deck = new Deck();
        player = new Player("플레이어");
        dealer = new Player("딜러");

        Console.Clear();
        Console.WriteLine("=== 블랙잭 게임을 시작합니다 ===");

        DealInitialCards();
        PlayerTurn();

        if (player.GetScore() <= 21)
            DealerTurn();

        ShowResult();
    }

    // 처음 카드 두 장씩 배분
    private void DealInitialCards()
    {
        player.ClearHand();
        dealer.ClearHand();

        player.AddCard(deck.DrawCard());
        dealer.AddCard(deck.DrawCard());
        player.AddCard(deck.DrawCard());
        dealer.AddCard(deck.DrawCard());

        player.ShowHand();
        dealer.ShowHand(false); // 딜러는 한 장 숨김
    }

    // 플레이어 턴
    private void PlayerTurn()
    {
        while (true)
        {
            Console.WriteLine("\n카드를 더 받으시겠습니까? (H: Hit, S: Stand)");
            string input = Console.ReadLine()?.ToLower();

            if (input == "h")
            {
                player.AddCard(deck.DrawCard());
                player.ShowHand();

                if (player.GetScore() > 21)
                {
                    Console.WriteLine("버스트! 21점을 초과했습니다.");
                    return;
                }
            }
            else if (input == "s")
            {
                return;
            }
            else
            {
                Console.WriteLine("잘못된 입력입니다. H 또는 S를 입력해주세요.");
            }
        }
    }

    // 딜러 턴 (17 이상일 때 멈춤)
    private void DealerTurn()
    {
        Console.WriteLine("\n딜러의 턴 시작");
        dealer.ShowHand();

        while (dealer.GetScore() < 17)
        {
            Console.WriteLine("딜러가 카드를 받습니다...");
            dealer.AddCard(deck.DrawCard());
            dealer.ShowHand();
        }

        if (dealer.GetScore() > 21)
            Console.WriteLine("딜러 버스트! 21점을 초과했습니다.");
        else
            Console.WriteLine("딜러가 멈췄습니다.");
    }

    // 최종 결과 출력
    private void ShowResult()
    {
        int playerScore = player.GetScore();
        int dealerScore = dealer.GetScore();

        Console.WriteLine("\n=== 게임 결과 ===");
        player.ShowHand();
        dealer.ShowHand();

        if (playerScore > 21)
            Console.WriteLine("패배하셨습니다.");
        else if (dealerScore > 21 || playerScore > dealerScore)
            Console.WriteLine("승리하셨습니다!");
        else if (playerScore < dealerScore)
            Console.WriteLine("패배하셨습니다.");
        else
            Console.WriteLine("무승부입니다.");
    }
}

// 프로그램 진입점
class Program
{
    static void Main(string[] args)
    {
        while (true)
        {
            GameManager game = new GameManager();
            game.StartGame();

            Console.WriteLine("\n다시 시작하시겠습니까? (Y/N)");
            string input = Console.ReadLine()?.ToLower();
            if (input != "y")
                break;
        }

        Console.WriteLine("게임을 종료합니다.");
    }
}

 

와,,너무 어렵다.. 하나씩 모르는거 다시 정리해야겠다...

 

✅ 1. 자동 구현 프로퍼티는 뭘까?

public Suit Suit { get; }
public Rank Rank { get; }

처음에는 { get; }만 써 있는 게 뭔지 잘 몰랐다. 근데 이건 자동 구현 프로퍼티라고 해서
별도로 필드를 선언하지 않아도, C#이 자동으로 내부에 저장공간을 만들어주는 기능이었다!

즉, 아래 코드처럼

private Suit _suit;
public Suit Suit { get { return _suit; } }

간단하게 한 줄로 줄여서 표현한 게 바로:

public Suit Suit { get; }

이런 식으로 get만 있으면 외부에서 읽기만 가능하고, 값을 바꾸는 건 불가능하다는 뜻이다.
초기화는 생성자에서만 가능하다.


✅ 2. Rank는 enum인데 Ace가 들어오면 어떻게 11이 되는 걸까?

public enum Rank
{
    Two = 2, Three, ..., Jack = 10, Queen = 10, King = 10, Ace = 11
}

처음엔 enum이라는 게 단순히 이름 붙은 그룹이라고 생각했는데,
이렇게 = 숫자를 붙여주면 실제로 숫자 값을 가질 수 있다는 걸 알게 됐다!

그리고 아래 코드를 보면:

public int GetValue() => (int)Rank;

여기서 Rank는 enum인데, (int)Rank라고 형변환을 해주면
정수 값으로 바뀌어서 리턴된다. 그래서 Rank.Ace가 들어오면 11이 되는 거다!
오... 이런 식으로 enum은 이름을 갖고 있지만 숫자로도 바꿀 수 있는건 몰랐다!


✅ 3. foreach (Suit suit in Enum.GetValues(typeof(Suit))) 이건 어려웠다...

처음엔 이 한 줄이 뭔 말인지 하나도 안 보였다.
근데 하나씩 뜯어보니까 이렇게 해석할 수 있었다:

  • Enum.GetValues(typeof(Suit))
    → Suit 열거형에 정의된 값들을 전부 가져온다. 결과는 배열로 나온다.
    → 즉 [Hearts, Diamonds, Clubs, Spades] 이런 배열이 생김
  • foreach (Suit suit in ...)
    → 그 배열을 하나씩 꺼내서 suit 변수에 담아 반복한다는 뜻

결국 이 코드는 모든 무늬(Suit)를 하나씩 꺼내서 루프를 돌리는 것이다!

foreach (Suit suit in Enum.GetValues(typeof(Suit)))
{
    // suit는 Hearts → Diamonds → Clubs → Spades 순으로 반복
}

와... 처음엔 진짜 괴랄했는데 알고 나니까 엄청 유용한 문법이었다 😅


✅ 4. (cards[i], cards[j]) = (cards[j], cards[i]); 이건 뭐지?

처음에는 그냥 한 줄 스왑처럼 보였는데, 진짜로 되는 건가 싶었다.
찾아보니까 이건 튜플 스왑 문법이었고, C# 7.0 이상부터 지원되는 기능이었다!

즉, 아래 코드랑 똑같은 기능이다:

var temp = cards[i];
cards[i] = cards[j];
cards[j] = temp;

하지만 위 코드는 3줄이고, 아래는 단 1줄:

(cards[i], cards[j]) = (cards[j], cards[i]);

이게 된다니... 정말 깔끔하고 예쁘다!
근데 처음 봤을 땐 이게 뭐야? 싶을 정도로 생소했던 문법이었다!

사실 난 temp로 그냥 쓰는게 더 익숙해서..잘 기억해놨다가 써먹어야겠다!


✅ 5. 그 외에도 새롭게 이해한 문법들

Card card = cards[0];
cards.RemoveAt(0);
return card;

→ 이건 덱에서 맨 앞에 있는 카드 한 장을 꺼내고, 덱에서 제거해서 리턴하는 부분이다.

public List<Card> Hand { get; private set; } = new List<Card>();

→ 플레이어의 카드 리스트. 외부에서는 읽기만 가능하고 수정은 불가능.

foreach (var card in Hand)
{
    int value = card.GetValue();
    score += value;
    if (card.Rank == Rank.Ace) aceCount++;
}

→ 카드 하나씩 꺼내서 점수 누적하고, Ace가 몇 개인지도 따로 세서
21 초과 시 점수 조정할 수 있도록 준비하는 로직이다.

 

이번 블랙잭 게임을 만들면서 솔직히 다 만들지는 못했지만 모르는 문법도 꽤 있었다.

너무 어려웠지만 하나씩 차근차근 하면서 다시 또 배우면 된다!

항상 겸손하게 하나하나씩 천천히 배워가자! 개발자는 겸손할 수 밖에 없는 직업이기 때문이다.. 모르는게 너무많아..ㅠㅜ

반응형
반응형

🗓️ 오늘 하루 일정

✅ 오전

  • 09:00 ~ 09:10 : 팀원들과 소통 및 자기소개
  • 09:10 ~ 11:30 : 개인 공부
    • 스네이크 게임 제작
    • 맵 구현, Snake 리스트로 표현, 키 입력 처리 구현 등
  • 11:30 ~ 13:00 : C# 체크리스트 강의 수강
    • 변수와 자료형, ConsoleKey, 콘솔 단축키 (Ctrl+Shift+F, Ctrl+R+R, Shift+F12) 등 학습

🍽️ 점심시간

  • 13:00 ~ 14:00 : 점심시간

✅ 오후

  • 14:00 ~ 18:00 : 개인 공부 및 Snake 게임 마무리
    • 튜플, 리스트, ConsoleKey, Thread.Sleep(), snake.Clear() 등 이해
    • 게임 로직 완성 및 클래스 다이어그램 정리
    • 스택 프레임과 함수 호출 구조 시각화 학습
    • LINQ, Any(), 람다식 구조 심화 학습

🍽️ 저녁시간

  • 18:00 ~ 19:00 : 저녁시간

✅ 저녁

  • 19:00 ~ 21:00 : 복습 및 실습
    • Item 클래스 직접 구현 (아이템 추가 / 출력 기능)
    • 중복 검사 (Any() + 람다식)
    • 오늘 전체 개념 복습 정리

✅ 오늘 학습 키워드

🎮 Snake 게임 제작 관련

  1. 콘솔에서 키보드 입력 받는 방법
  2. ConsoleKey, Console.ReadKey(true) 사용법
  3. Thread.Sleep()으로 속도 조절
  4. Clear(), SetCursorPosition() 사용 이유
  5. static ConsoleKey direction = ConsoleKey.RightArrow; 선언 이유
  6. List<(int x, int y)> 형태로 Snake 위치 저장
  7. (int x, int y) newHead = head; 라인에서 2줄로 쓴 이유
  8. y--가 위로, y++가 아래로 가는 이유
  9. 게임 로직 전체 흐름 (이동, 충돌, 먹이 처리)

🗂️ 컬렉션/자료구조 & LINQ

  1. 리스트 안에 튜플 저장 (List<(int, string)>)
  2. Any() 함수로 중복 검사
  3. Find(), FindAll(), Where() 차이
  4. 람다식에서 x => 문법 의미
  5. 튜플에서 Item1, Item2가 의미하는 것
  6. .name, .age 출력에서 + 안 써서 생긴 실수 고치기

🧠 이론 개념 학습

  1. 스택프레임 개념 설명 (호출 순서, 메모리 흐름)
  2. 스택프레임 vs 스택의 차이점

🧪 실습 및 확장

  1. ItemManager 클래스 구조 분석
  2. AddItem()에서 중복 방지 로직 구현
  3. item.Any(x => x.itemNumber == newItem.itemNumber) 상세 설명
  4. x의 정체와 람다식 구조 해설
  5. 콘솔 기반 아이템 관리 시스템 직접 구현 (Add, Show)
  6. ItemInstance 클래스 & ItemManager 기능 분석

📌 기타

  1. Ctrl + Shift + F, Ctrl + R, R, Shift + F12 단축키 학습
  2. 클래스 다이어그램(classDiagram) 구조 정리
  3. class SnakeGame UML 다이어그램 문법 교정

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

 

C# - Console SnakeGame(콘솔 스네이크 게임) + 활용 예제

이번 과제로 스네이크 게임을 만들게 되었다 만들면서 느낀거지만.... 코딩테스트 준비를 열심히 해야겠다는 생각이 들었다ㅠㅜ.. GPT의 도움을 많이 받았고 이걸 통해서 활용 예제도 많이 다뤄

dev-jen.tistory.com

 

C# - Tuple(튜플) vs Struct(구조체)

✅ 튜플이란?서로 다른 타입의 데이터를 한 번에 묶어서 저장할 수 있는 구조배열이나 리스트처럼 하나의 타입만 저장하는 게 아니라,여러 값을 하나의 단위로 다룰 수 있어🔸 기본 예제var perso

dev-jen.tistory.com

 

C# - ConsoleKey(콘솔키)

✅ ConsoleKey란?ConsoleKey는 C#에서 키보드의 실제 키를 표현하기 위한 **열거형(enum)**이야.예를 들어, 키보드의 화살표 키, Enter, A, B, 숫자 1~9, ESC 등은모두 ConsoleKey의 값으로 표현할 수 있어.✅ 사용

dev-jen.tistory.com

 

C# - List(tuple) vs Dictionary

한참 리스트의 튜플로 코드로 장난치고 있었는데 그럼 딕셔너리와의 엄청난 치이점이 뭘까.. 싶었다물론 리스트와 딕셔너리도 비슷하게 사용할 수 있다.List items = new List{ (0, "나무칼"), (1, "나무

dev-jen.tistory.com

 

C# - 스택 프레임이란? (Stack Frame) vs 스택(Stack)

🧠 스택 프레임이란?함수를 호출할 때마다 생기는 임시 메모리 공간이자 작업 단위 블록각 함수마다 **자신만의 공간(프레임)**을 갖고, 거기서 매개변수, 지역 변수, 복귀 주소 등을 저장해✅

dev-jen.tistory.com

 

C# - Linq(Language Integrated Query)

🧠 LINQ란?LINQ는 "Language Integrated Query"의 약자로,C# 코드 안에서 SQL처럼 데이터를 쉽게 조회하고 가공할 수 있게 해주는 문법이야.✅ LINQ의 목적배열, 리스트, 딕셔너리, 데이터베이스 등 여러 종류

dev-jen.tistory.com

 

C# - C#으로 간단한 아이템 매니저 구현하기 - List와 LINQ로 컬렉션 관리 연습

public class ItemInstance{ public int id; // 유니크한 아이디 public int itemId; // 같은 종류의 아이템끼리 공유하는 ID // 기타 필요한 속성들...}public class ItemManager{ private List _items; public ItemManager(List items) { _ite

dev-jen.tistory.com

 

🎮 Snake 게임 구현 관련

오늘 만든 콘솔 기반 스네이크 게임은 생각보다 구현 요소가 많았다.

처음엔 단순히 "뱀이 움직인다"는 걸 만들면 끝일 줄 알았는데,

키 입력 처리, 이동 방향 저장, 충돌 판정, 맵 초기화, 먹이 생성게임 로직의 핵심 요소들이 하나씩 모여서 게임이 만들어진다는 걸 체감했다.

Console.ReadKey(true)로 키를 숨기면서 입력받는 방법,

Console.SetCursorPosition()으로 콘솔 깜빡임을 없애는 방법 등도 실제로 구현해보니 콘솔이 단순한 UI가 아니라는 걸 알게 됐다.

기술 하나하나가 게임 흐름을 만든다는 걸 느꼈다.


🧱 튜플과 리스트

튜플은 정말 유용했다!

클래스를 만들지 않아도 (int, string, string) 이런 식으로 여러 값을 하나로 묶어서 리스트에 넣을 수 있었고,

필드를 .Item1로도 접근할 수 있지만 (itemNumber, itemName)처럼 이름 붙이면 훨씬 보기 좋았다.

튜플 리스트를 활용하면, 간단한 구조체 같은 느낌으로 데이터를 깔끔하게 표현할 수 있다는 걸 처음 알게 됐다.


🔎 LINQ와 람다식

LINQ는 조금 어려웠지만 잘 사용한다면 정말 유용할거 같았다.

Any()는 특정 조건을 만족하는 값이 있는지 쉽게 확인해주고,

x => x.itemNumber == newItem.itemNumber 같은 람다식은 처음엔 낯설었지만,

"리스트 속 요소 하나를 x라고 부르면서 조건을 검사하는 방식"이라고 이해하니까 정리가 됐다.

이전에는 foreach로 돌렸던 걸 한 줄로 쓸 수 있다는 건 생산성과 가독성 모두에서 큰 차이를 만든다.


🧠 스택프레임과 함수 호출 흐름

스택프레임은 사실 처음엔 뭐지? 싶었다.

그냥 '함수는 호출하면 실행되고 끝나는 거 아닌가?' 정도로만 알고 있었는데,

함수가 호출될 때 메모리 위에 쌓이는 구조, 그리고 함수가 끝나면 사라지고,

그 다음 함수로 돌아가는 흐름과 구조를 시각적으로 배우니까 메모리 구조에 대한 이해가 확 생겼다.

프로그래밍은 결국 메모리와 흐름을 다루는 일이라는 걸 또 한 번 느꼈다.


🧪 아이템 관리 실습

콘솔에서 직접 AddItem, ShowItemList 같은 메뉴를 구성해서 만들고,

중복 체크를 위해 Any()를 적용해본 건 매우 실용적이었다.

내가 만든 코드 안에 LINQ를 직접 써봤다는 점에서 배운 걸 바로 실전에서 활용했다는 확신이 생겼다.


🔧 도구와 기타 학습

Visual Studio 단축키도 오늘 새롭게 알게 됐는데,

Ctrl + Shift + F로 전체 찾아 바꾸기, Ctrl + R, R로 이름 바꾸기, Shift + F12로 참조 찾기 등

생산성 도구는 작업 속도와 코드 탐색에 진짜 유용하다는 걸 체감했다.


오늘 하루는 정말 많은 걸 배우고 느낀 날이었다.

튜플, LINQ, Snake 게임 구현, 스택프레임 구조까지 하나하나의 배움이 앞으로의 개발에 확실히 도움이 많이 될 듯 헀다.


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

1. 리스트를 사용해봤지만 아직 익숙하지 않았다

  • 문제정의
  • 리스트에 튜플을 넣고 반복문으로 순회하거나, 원하는 값을 조건으로 찾는 방법이 헷갈렸다.
  • 시도
  • List<(int itemNum, string name)> 형식으로 선언하고 foreach로 하나씩 꺼내보며 직접 실험했다.
  • 해결 방법
  • 튜플 안의 필드에 이름을 붙이면 .itemNum, .name처럼 직관적으로 접근할 수 있다는 걸 알게 됐다.
  • 새롭게 알게 된 점
  • 튜플은 간단한 구조를 저장할 때 강력한 도구이고, 필드 이름을 붙이는 게 가독성과 활용성을 높여준다.

2. ConsoleKey direction = ConsoleKey.RightArrow; 문법이 낯설었다

  • 문제정의
  • ConsoleKey라는 타입이 무엇인지, RightArrow 같은 값이 어떻게 저장되는지 감이 안 왔다.
  • 해결 방법
  • ConsoleKey는 키보드의 특정 키를 나타내는 열거형(Enum) 이며, Console.ReadKey()로 해당 키 값을 읽어 비교하는 데 쓰인다는 걸 배웠다.
  • 새롭게 알게 된 점
  • 방향이나 키 입력을 다룰 때 ConsoleKey가 유용하다는 걸 처음으로 체감했다.

3. Thread.Sleep(150)과 Console.Clear()의 기능

  • 문제정의
  • 콘솔 게임 코드에서 이 코드들이 무슨 역할을 하는지 몰랐다.
  • 해결 방법
  • Thread.Sleep은 게임 루프의 실행 속도를 조절하고, Console.Clear는 이전 화면을 지워서 다음 화면으로 바꿔주는 용도임을 알게 됐다.
  • 새롭게 알게 된 점
  • 콘솔 게임에서는 출력 순서와 타이밍 조절이 매우 중요하다는 것을 배웠다.

4. 방향 전환 제한 조건식이 이해되지 않았다

if ((key == ConsoleKey.UpArrow && direction != ConsoleKey.DownArrow) ||
    (key == ConsoleKey.DownArrow && direction != ConsoleKey.UpArrow) ||
    (key == ConsoleKey.LeftArrow && direction != ConsoleKey.RightArrow) ||
    (key == ConsoleKey.RightArrow && direction != ConsoleKey.LeftArrow))
  • 문제정의
  • 위 코드가 정확히 무슨 내용인지 헷갈렸다.
  • 해결 방법
  • 현재 방향과 정반대 방향으로는 이동할 수 없게 막는 조건이라는 걸 알게 됐다.
  • 새롭게 알게 된 점
  • 이 조건은 뱀이 자기 몸과 부딪히지 않도록 막아주는 기본적인 안전 장치라는 걸 이해했다.

5. snake[0]을 한 줄로 newHead에 안 넣고 두 줄로 나눈 이유

  • 문제정의
  • var head = snake[0]; (int x, int y) newHead = head; 이 구조가 비효율적으로 느껴졌다.
  • 해결 방법
  • 의도를 분리하고, 변수명을 통해 역할을 구분하기 위한 구조라는 걸 이해했다.
  • 새롭게 알게 된 점
  • 코드가 짧은 것보다 명확한 의도 표현이 더 중요하다는 걸 느꼈다. 그리고 저렇게 사용하는게 더 안전한 상태라는걸 알게됐다!

6. Any()와 람다식 구조가 처음엔 낯설었다

  • 문제정의
  • item.Any(x => x.itemNumber == newItem.itemNumber)에서 x가 뭔지, =>는 무슨 의미인지 몰랐다.
  • 해결 방법
  • x는 리스트 안의 요소 하나를 나타내는 이름이고, =>람다식을 사용해서 정의하면 된다.
  • 새롭게 알게 된 점
  • Any()는 조건을 만족하는 요소가 있는지를 한 줄로 쉽게 확인할 수 있는 유용한 도구다.

📝 메모

class Item
{
    // 아이템 정보를 저장하는 리스트
    static List<(int itemNumber, string itemName, string itemInfo)> item = new List<(int itemNumber, string itemName, string itemInfo)>();

    static bool isStop = false;

    static void Main(string[] args)
    {

        while (!isStop)
        {
            Display();
        }

    }

    static void Display()
    {
        Console.WriteLine("1번 아이템 추가");
        Console.WriteLine("2번 아이템 목록");
        Console.WriteLine("0번 프로그램 종료");
        Console.Write("번호 선택 : ");
        int Sel = int.Parse(Console.ReadLine());


        switch (Sel)
        {
            case 1:
                Console.WriteLine("아이템 추가");
                AddItem();
                break;
            case 2:
                Console.WriteLine("아이템 목록");
                ShowItemList();
                break;
            case 0:
                Console.WriteLine("프로그램 종료");
                break;
            default:
                Console.WriteLine("잘못된 선택입니다. 다시 시도하세요.");
                break;
        }
    }

    static void AddItem()
    {
        (int itemNumber, string itemName, string itemInfo) newItem;
        Console.Write("아이템 번호 입력 : ");
        newItem.itemNumber = int.Parse(Console.ReadLine());
        
        // 중복 검사
        bool exists = item.Any(x => x.itemNumber == newItem.itemNumber);
        if (exists)
        {
            Console.WriteLine("⚠️ 이미 존재하는 아이템 번호입니다. 추가할 수 없습니다.");
            return;
        }

        Console.Write("아이템 이름 입력 : ");
        newItem.itemName = Console.ReadLine();

        Console.Write("아이템 정보 입력 : ");
        newItem.itemInfo = Console.ReadLine();

        item.Add(newItem);
        Console.WriteLine("✅ 아이템이 추가되었습니다.");
    }


    static void ShowItemList()
    {
        foreach (var newitem in item)
        {
            Console.WriteLine($"아이템 번호 : {newitem.itemNumber}");
            Console.WriteLine($"아이템 이름 : {newitem.itemName}");
            Console.WriteLine($"아이템 정보 : {newitem.itemInfo}");
            Console.WriteLine("-------------------------");
        }

    }
}

마지막 9시 되기전 아이템에 대한 코드 조금 끄적여봤다..ㅎㅎ..

 

오늘은 정말 많이 배우고, 또 스스로 몰랐던 걸 하나씩 마주했던 하루였다.

리스트와 튜플은 익숙하다고 생각했는데, 막상 실습해보니 생소한 부분이 많았고,

Any()나 람다식 같은 문법은 처음엔 이해하기 어려웠다.

하지만 코드 하나하나를 직접 써보고, 출력 결과를 확인하면서

“많이 모르고 있었구나”를 진심으로 느꼈고

“하나씩 차근차근 하자”는 마음으로 계속 물어보면서 배워갔다.

스네이크 게임을 만들면서 콘솔에서도 충분히 재미있고 구조적인 게임을 만들 수 있다는 걸 깨달았고,

그 과정에서 ConsoleKey, Thread.Sleep, Clear, switch, 조건 분기, 중복 검사 등

정말 다양한 개념을 직접 써보며 손에 익힐 수 있었던 것도 큰 수확이었다.

오늘 내가 배운 건 단순한 C# 문법이 아니라

생각하고 해석하는 힘, 그리고 조금씩 완성해나가는 자신감이었다.

힘들었지만 그만큼 보람찼고, 코드를 통해 사고하는 방식이 하루하루 깊어지는 게 느껴진다.

하여튼 내일도 화이팅!!!!


반응형
반응형
public class ItemInstance
{
    public int id;        // 유니크한 아이디
    public int itemId;    // 같은 종류의 아이템끼리 공유하는 ID
    // 기타 필요한 속성들...
}

public class ItemManager
{
    private List<ItemInstance> _items;

    public ItemManager(List<ItemInstance> items)
    {
        _items = items;
        UnityEngine.Debug.LogError(_items.Count); // 아이템 개수 로그 (유니티 콘솔)
    }

    // 1. id로 단일 아이템 찾기
    public ItemInstance GetItem(int id)
    {
        return _items.Find(x => x.id == id);
    }

    // 2. itemId로 단일 아이템 찾기
    public ItemInstance GetItemByItemId(int itemId)
    {
        return _items.Find(x => x.itemId == itemId);
    }

    // 3. itemId로 복수의 아이템 찾기
    public List<ItemInstance> GetItemsByItemId(int itemId)
    {
        return _items.FindAll(x => x.itemId == itemId);
    }

    // 4. 전체 아이템 리스트 반환
    public List<ItemInstance> GetAllItems()
    {
        return _items;
    }

    // 5. 특정 조건으로 필터링된 리스트 반환 (예시)
    public List<ItemInstance> GetItemsByCondition(System.Func<ItemInstance, bool> condition)
    {
        return _items.Where(condition).ToList();
    }

    // 6. 아이템 추가
    public void AddItem(ItemInstance item)
    {
        _items.Add(item);
    }

    // 7. 아이템 제거
    public bool RemoveItem(int id)
    {
        var item = GetItem(id);
        if (item != null)
        {
            _items.Remove(item);
            return true;
        }
        return false;
    }
}

var manager = new ItemManager(new List<ItemInstance> {
    new ItemInstance { id = 1, itemId = 1001 },
    new ItemInstance { id = 2, itemId = 1002 },
    new ItemInstance { id = 3, itemId = 1001 },
});

var item = manager.GetItem(2); // id가 2인 아이템
var sameKind = manager.GetItemsByItemId(1001); // itemId가 1001인 아이템들

 

🔍 이 코드의 개념 요약

1. 📦 ItemInstance 클래스

  • 아이템 하나를 표현하는 데이터 구조
  • 각 아이템마다 id(고유 식별자), itemId(아이템 종류 식별자)를 갖고 있음
public class ItemInstance
{
    public int id;        // 고유 아이디
    public int itemId;    // 아이템 종류 아이디
}

2. 🧰 ItemManager 클래스

  • 여러 개의 ItemInstance를 List로 가지고 있음
  • 해당 리스트를 관리하기 위한 도우미 메서드들을 제공
private List<ItemInstance> _items;

3. 🧠 핵심 기능 설명 (LINQ + 컬렉션 활용)

✅ 단일 아이템 검색 (Find)

_items.Find(x => x.id == id);
  • 조건에 맞는 첫 번째 요소 하나만 반환
  • 내부적으로는 foreach와 유사한 탐색

✅ 복수 아이템 검색 (FindAll)

_items.FindAll(x => x.itemId == itemId);
  • 조건을 만족하는 모든 아이템을 리스트로 반환

✅ 조건 기반 검색 (Where)

_items.Where(condition).ToList();
  • 더 유연한 조건식 사용 가능
  • 예: x => x.itemId > 1000 && x.id % 2 == 0

✅ 아이템 추가 & 제거

_items.Add(item); // 추가
_items.Remove(item); // 객체로 제거

💡 실용 포인트

기능 설명 사용 메서드
고유 ID로 검색 한 아이템만 조회 Find()
종류 ID로 검색 동일한 종류 여러 개 조회 FindAll()
동적 조건 검색 필터 조건을 외부에서 전달 Where()
추가 / 삭제 동적으로 아이템을 관리 Add(), Remove()

🧪 사용 예시

var manager = new ItemManager(new List<ItemInstance> {
    new ItemInstance { id = 1, itemId = 1001 },
    new ItemInstance { id = 2, itemId = 1002 },
    new ItemInstance { id = 3, itemId = 1001 },
});

var item = manager.GetItem(2); // ID 2인 아이템 하나
var sameKind = manager.GetItemsByItemId(1001); // itemId가 1001인 아이템들

 

게임 개발에서 자주 등장하는 "아이템 관리 시스템"을 LINQ와 컬렉션으로 구현해봤다.
작은 데이터 구조와 조건 검색부터 시작하면, 나중에 인벤토리 시스템이나 DB 연동으로도 확장할 수 있다.
앞으로는 Dictionary나 GroupBy() 같은 고급 기능도 적용해보고 싶다!

반응형
반응형

🧠 LINQ란?

LINQ는 "Language Integrated Query"의 약자로,
C# 코드 안에서 SQL처럼 데이터를 쉽게 조회하고 가공할 수 있게 해주는 문법이야.


✅ LINQ의 목적

  • 배열, 리스트, 딕셔너리, 데이터베이스 등 여러 종류의 컬렉션에서
  • 데이터를 필터링, 정렬, 변형, 집계할 수 있음
  • 반복문 없이 간결하고 가독성 좋은 코드로 처리 가능

📌 LINQ 기본 문법 (2가지 방식)

  1. 메서드 체이닝 방식 (람다식 기반)
  2. 쿼리 식(Query Expression) 방식 (SQL처럼 생김)

✅ 예제: 리스트에서 짝수만 고르기

🔹 1. 메서드 체이닝 방식

List<int> numbers = new List<int> { 1, 2, 3, 4, 5, 6 };

var evenNumbers = numbers.Where(n => n % 2 == 0);

foreach (var num in evenNumbers)
{
    Console.WriteLine(num);  // 2, 4, 6
}
  • Where : 조건 필터링
  • n => n % 2 == 0 : 짝수 조건 (람다식)

🔹 2. 쿼리 식 방식

var evenNumbers = from n in numbers
                  where n % 2 == 0
                  select n;

foreach (var num in evenNumbers)
{
    Console.WriteLine(num);  // 2, 4, 6
}

마치 SQL 쿼리를 C# 안에 집어넣은 것처럼 쓸 수 있음


🎯 자주 쓰는 LINQ 함수 요약

함수 설명
Where() 조건 필터링
Select() 변형 (가공)
OrderBy() / OrderByDescending() 정렬
First() / FirstOrDefault() 첫 번째 요소
Any() / All() 조건 만족 여부
Count() / Sum() / Max() / Min() 집계 연산
ToList() 결과를 리스트로 반환

 


✅ 예제: 아이템 목록에서 특정 조건 필터링

var items = new List<(int id, string name)>
{
    (0, "나무칼"),
    (1, "철검"),
    (2, "다이아검"),
    (3, "나무방패")
};

// 이름에 '나무'가 들어간 아이템만 뽑기
var woodItems = items.Where(item => item.name.Contains("나무"));

foreach (var item in woodItems)
{
    Console.WriteLine($"{item.id}: {item.name}");
}

💡 LINQ의 장점

  • 반복문 없이 간결하게 표현 가능
  • 가독성 향상
  • 다양한 데이터 소스(배열, List, DB 등)에 적용 가능

❗ 주의할 점

  • LINQ는 **지연 실행(lazy evaluation)**을 기본으로 한다
    → foreach, ToList() 같은 걸로 결과를 써야 실제로 실행됨
  • 무거운 연산을 LINQ로 처리할 땐 성능을 고려해야 함

예전에 웹 개발 배웠을때 SQL을 배웠었는데 언젠간 쓸 일이 있지 않을까 싶다 메서드 체이닝 방식도

저번에 배웠던 델리게이트 람다식이랑 느낌이 비슷한데 그건 이제 함수를 변수처럼 사용하는거고

이거는 약간 그냥 람다식 처럼 쓰는거 같다!

반응형
반응형

🧠 스택 프레임이란?

함수를 호출할 때마다 생기는 임시 메모리 공간이자 작업 단위 블록
각 함수마다 **자신만의 공간(프레임)**을 갖고, 거기서 매개변수, 지역 변수, 복귀 주소 등을 저장해


✅ 스택 메모리와의 관계

  • 스택(Stack): LIFO 구조로, 함수가 호출될수록 위로 쌓임
  • 함수가 종료되면 스택 프레임이 제거됨
  • → 즉, 스택 프레임은 함수의 실행 상태를 담는 메모리 상자

📦 스택 프레임 구성 요소 (간단히)

  1. 매개변수 (Parameters)
  2. 지역 변수 (Local variables)
  3. 복귀 주소 (Return address)
    • 함수가 끝났을 때, 어디로 돌아갈지 저장
  4. 이전 스택 프레임 포인터
    • 호출한 함수의 위치 기억 (연결)

🔁 함수 호출 예시

static void Main()
{
    SayHello("재은");
}

static void SayHello(string name)
{
    string message = $"Hello, {name}";
    Console.WriteLine(message);
}

📐 호출 흐름과 스택 프레임 생성 순서

  1. Main()이 실행됨 → Main의 스택 프레임 생성
  2. SayHello("재은") 호출됨 → 새로운 스택 프레임 생성
    • name = "재은" 저장
    • message라는 지역 변수 저장
  3. SayHello() 실행 완료 → SayHello 프레임 제거
  4. 다시 Main으로 복귀

📊 시각적으로 보면

 
|-----------------------------| ← Top of Stack (최신 함수)
| SayHello 스택 프레임        |
|   - name = "재은"           |
|   - message = "Hello, 재은" |
|-----------------------------|
| Main 스택 프레임            |
|   - ...                     |
|-----------------------------| ← Bottom of Stack (시작 함수)

⚠️ 왜 중요한가?

  • 재귀 함수함수 호출 깊이를 이해하려면 필수
  • 스택 오버플로우는 너무 많은 스택 프레임이 쌓여서 발생
  • 디버깅 시 호출 순서(Call Stack) 분석에 사용됨
  • 지역 변수는 전부 이 스택 프레임 안에 존재함

✅ 요약

개념 설명
스택 프레임 함수가 호출될 때 생성되는 메모리 공간
생성 위치 스택 메모리 영역 (LIFO 구조)
저장 내용 매개변수, 지역변수, 복귀주소 등
생명 주기 함수 호출 시 생성 → 종료 시 제거
관련 이슈 재귀 깊이 초과 → 스택 오버플로우 발생

흠 결국에는 그냥 호출되는 함수나 변수 등등의 값들이 스택처럼 쌓이는 프레임이라고 봐도 되는건가 싶다.

 

한 줄 요약하자면:

💡 스택 프레임 = 함수 실행 정보를 스택처럼 저장하는 구조

함수 호출 → 스택에 쌓이고
함수 종료 → 스택에서 제거됨


📌 그래서 이런 구조 때문에,

  • 재귀 함수에서 호출이 너무 많아지면 → 스택 오버플로우 발생
  • 디버깅할 때 Call Stack 보면 → 어떤 함수가 어떤 순서로 호출됐는지 알 수 있음 

그렇다고 합니다! 그렇게 어려운 개념은 아닌거 같아요!

근데 이제 갑자기 궁금해지는게 스택과 스택프레임은 뭐가 다른건지.. 궁금해졌습니다!!!!

Q. 그러면 스택과 스택프레임의 차이가 뭐지? 뭐가 다른거야? 다른 개념인거야?

 

📌 스택 vs 스택 프레임 차이

구분 📦 스택 (Stack) 🧱 스택 프레임 (Stack Frame)
개념 메모리 공간의 한 영역 (LIFO 구조) 함수 호출 시 스택에 쌓이는 한 칸의 정보 단위
용도 함수 호출 정보들을 저장하는 전체 공간 하나의 함수 호출에 대한 지역 변수, 매개변수, 복귀 주소 등 저장
관계 여러 개의 스택 프레임이 쌓이는 기반 구조 스택 안에 쌓이는 하나하나의 단위
예시 함수가 호출될 때마다 메모리 스택 위에 프레임을 하나씩 추가 Main(), Func1(), Func2() 각각이 스택 프레임으로 쌓임
구조 시스템이 제공하는 메모리 공간 (Stack segment) 함수 호출이 발생할 때마다 자동으로 생성됨
 

🎯 쉽게 비유하면?

🔸 스택은 책을 쌓는 책장이고,
🔸 스택 프레임은 그 책장에 하나씩 올려지는 책 한 권이야!

즉,

  • 스택은 전체 공간
  • 스택 프레임은 그 안에 들어가는 함수 호출 단위

📍 C#에서 둘 다 언제 볼 수 있어?

  • 스택 (Stack): 함수 호출할 때마다 자동으로 사용됨 (우리가 직접 신경 안 써도 됨)
  • 스택 프레임: 디버깅 중 Call Stack 보거나, 재귀로 스택 오버플로우 나면 실감함

✅ 요약 한 줄

스택은 메모리 공간이고,
스택 프레임은 그 공간에 함수 호출 시 생성되는 작업 단위다!

 


오오 그렇다고 합니다 저희가 알고있는 스택은 그냥 전체적인 스택의 메모리 공간을 얘기하는 듯 하고

스택 프레임은 그안에 쌓이는 함수나 여러 메모리등을 얘기하는거 같네요! 궁금증 해결!

 

https://learn.microsoft.com/en-us/archive/blogs/ericlippert/the-stack-is-an-implementation-detail-part-one

 

The Stack Is An Implementation Detail, Part One

Ask Learn Ask Learn Table of contents Read in English Add Add to plan Share via Facebook x.com LinkedIn Email Print Note Access to this page requires authorization. You can try signing in or changing directories. Access to this page requires authorization.

learn.microsoft.com

스택에 관한 문서입니다!

반응형

C# - List(tuple) vs Dictionary

Dev_Jen
|2025. 7. 8. 15:07
반응형

한참 리스트의 튜플로 코드로 장난치고 있었는데 그럼 딕셔너리와의 엄청난 치이점이 뭘까.. 싶었다

물론 리스트와 딕셔너리도 비슷하게 사용할 수 있다.

List<(int itemNum, string name)> items = new List<(int, string)>
{
    (0, "나무칼"),
    (1, "나무도끼"),
    (2, "나무창"),
    ...
};

이런식으로 리스트 튜플을 만들어서 만들다가 이렇게 되면 딕셔너리와 차이점이 뭐지? 싶었다

하지만 뭐 다들 아시다싶이 리스트 튜플로는 키와 값 으로 있는게 아니라 여러개를 더 넣을 수 있다.

List<(int itemNum, string name, string info)> items = new List<(int itemNum, string name, string info)>();


items.Add((0, "나무칼", "나무칼이다"));
items.Add((1, "돌칼", "돌로 만든 칼이다"));
items.Add((2, "철칼", "철로 만든 칼이다"));
items.Add((3, "금칼", "금으로 만든 칼이다"));


for(int i=0; i<items.Count; i++)
{
    (int a, string b, string info) item = items[i];

    if (item.a == 0)
    {
        Console.WriteLine($"아이템 이름 : {item.b},  아이템 정보 : {item.info}");
    }
}

뭐..이런식으로 아이템에 대한 정보를 더 넣을 수 있다.

✅ Dictionary<int, string>로 바꾸면?

Dictionary<int, string> items = new Dictionary<int, string>
{
    { 0, "나무칼" },
    { 1, "나무도끼" },
    { 2, "나무창" },
    ...
};

→ **key(아이템 번호)**로 빠르게 **name(아이템 이름)**을 찾아야 할 때 적합하다.

이런식으로 딕셔너리가 더 빠르다고 한다. 왜냐하면 키 값을 이용해서 아이템의 이름이나 정보를 찾아주기때문.

 

📊 리스트(튜플) vs 딕셔너리 차이점 정리

항목 List<(int, string)> (튜플 리스트) Dictionary<int, string> (딕셔너리)
✅ 구조 튜플의 나열 (Key, Value) 형태의 리스트 Key → Value로 직접 연결된 자료구조
✅ 순서 순서 유지 (0번, 1번, 2번...) 순서 없음 (Key 중심 접근)
✅ 키 중복 허용됨 (같은 키 여러 개 가능) 허용되지 않음 (같은 키 추가 시 예외)
✅ 접근 방식 반복문이나 조건문으로 탐색 Key를 직접 지정해 빠르게 접근
✅ 검색 속도 느림 (순차 탐색) 빠름 (Key 기반 해시 탐색)
✅ 사용 목적 - 순서대로 출력  

 

  • 조건 필터링
  • 중복 허용
    | - 특정 키로 빠르게 검색
  • 중복 없이 고유한 키 사용 |
    | ✅ 코드 예 | items[i].Item1 == 3 | items[3] |
    | ✅ 메모리 | 상대적으로 단순 | 키 해시 맵을 위해 조금 더 복잡 |

🧠 예를 들어 이런 상황이라면?

📌 리스트(튜플)를 쓰는 경우

List<(int id, string name)> items = new List<(int, string)>
{
    (0, "나무칼"),
    (1, "나무도끼"),
    (2, "나무창"),
    (2, "예비 나무창") // 같은 ID 중복 허용
};
  • 순서대로 출력하거나
  • 조건(if)으로 찾을 때 유리
  • 같은 아이템 여러 개 저장 가능

📌 딕셔너리를 쓰는 경우

Dictionary<int, string> items = new Dictionary<int, string>
{
    { 0, "나무칼" },
    { 1, "나무도끼" },
    { 2, "나무창" }
};

// 빠르게 이름 얻기
Console.WriteLine(items[2]); // "나무창"
  • 키로 빠르게 접근할 때 유리
  • id → name 관계가 고정일 때 적합
  • 키는 반드시 고유

🎯 한 줄 요약

리스트(튜플)는 순서와 조건 검색에 강하고,
딕셔너리는 빠른 검색과 고유한 키 관리에 강하다.

 

확실히 두개는 명확한 차이가 있는 듯 하다. 흠.. 그럼 만약 아이템에 대한 정보나 그런걸 빨리 찾고싶다면

당연히 딕셔너리를 써야곘지만?... 키, 값으로만 이루어져 있으니 아이템은 여러 정보가 있을텐데 예를 든다면

아이템 번호, 이름, 정보, 능력치 등등 이렇다면 리스트 튜플을 쓰는게 좋을듯 하다!

반응형

C# - ConsoleKey(콘솔키)

Dev_Jen
|2025. 7. 8. 14:37
반응형

✅ ConsoleKey란?

ConsoleKey는 C#에서 키보드의 실제 키를 표현하기 위한 **열거형(enum)**이야.
예를 들어, 키보드의 화살표 키, Enter, A, B, 숫자 1~9, ESC 등
모두 ConsoleKey의 값으로 표현할 수 있어.


✅ 사용 예시

ConsoleKeyInfo keyInfo = Console.ReadKey(true);
ConsoleKey key = keyInfo.Key;

if (key == ConsoleKey.LeftArrow)
{
    Console.WriteLine("왼쪽으로 이동");
}
  • Console.ReadKey() → 사용자가 누른 키를 읽음
  • .Key → 어떤 키인지 ConsoleKey로 반환
  • ConsoleKey.LeftArrow → 키보드 왼쪽 화살표

✅ 자주 사용하는 ConsoleKey 값

키보드 키 ConsoleKey값
↑ 위 화살표 ConsoleKey.UpArrow
↓ 아래 화살표 ConsoleKey.DownArrow
← 왼쪽 화살표 ConsoleKey.LeftArrow
→ 오른쪽 화살표 ConsoleKey.RightArrow
ESC 키 ConsoleKey.Escape
엔터 ConsoleKey.Enter
스페이스바 ConsoleKey.Spacebar
알파벳 A ConsoleKey.A
숫자 1 ConsoleKey.D1
숫자 0 ConsoleKey.D0

숫자 키는 D1, D2, ..., D9, D0이고
키패드 숫자는 NumPad1, NumPad2, ... 로 구분됨


✅ 실제 예제: 방향키 컨트롤

while (true)
{
    ConsoleKeyInfo keyInfo = Console.ReadKey(true);
    ConsoleKey key = keyInfo.Key;

    switch (key)
    {
        case ConsoleKey.UpArrow:
            Console.WriteLine("위");
            break;
        case ConsoleKey.DownArrow:
            Console.WriteLine("아래");
            break;
        case ConsoleKey.LeftArrow:
            Console.WriteLine("왼쪽");
            break;
        case ConsoleKey.RightArrow:
            Console.WriteLine("오른쪽");
            break;
        case ConsoleKey.Escape:
            Console.WriteLine("종료!");
            return;
    }
}

✅ 응용: ConsoleKey를 조건 비교에 쓰기

if (key == ConsoleKey.W || key == ConsoleKey.UpArrow)
{
    Console.WriteLine("위로 이동 (W or ↑)");
}

WASD 키와 화살표 키를 함께 인식할 수도 있어!


🎯 요약

특징 설명
타입 enum (열거형)
용도 어떤 키가 눌렸는지 판단
주요 메서드 Console.ReadKey().Key
대표 값 UpArrow, Enter, Escape, A, D1, Spacebar 등

이번에 스네이크 게임을 만들면서 알게된 내용이다.

유니티에서 만들때나 키보드 입력값 많이 써봤지 C# 콘솔에서도 키보드 입력값이 들어갈 줄 몰랐다 ㅎㅎ..

아직도 공부할게 산더미 인듯 하다 열심히하자아ㅏ!!!

반응형
반응형

✅ 튜플이란?

  • 서로 다른 타입의 데이터를 한 번에 묶어서 저장할 수 있는 구조
  • 배열이나 리스트처럼 하나의 타입만 저장하는 게 아니라,
    여러 값을 하나의 단위로 다룰 수 있어

🔸 기본 예제

var person = ("Alice", 25);
Console.WriteLine(person.Item1); // "Alice"
Console.WriteLine(person.Item2); // 25

여기서 나오는 Item1,2,3도 궁금해서 찾아봤다.

✅ 기본 튜플 (이름 없이 생성)

var tuple = ("Apple", 123, true);

Console.WriteLine(tuple.Item1); // "Apple"
Console.WriteLine(tuple.Item2); // 123
Console.WriteLine(tuple.Item3); // true
  • Item1, Item2, Item3는 자동으로 붙는 이름이야
  • 이름을 따로 정하지 않았기 때문에 이런 식으로 접근해야 해

✅ 이름 있는 튜플 (직접 이름 붙이기)

(string name, int price, bool inStock) product = ("Apple", 123, true);

Console.WriteLine(product.name);     // "Apple"
Console.WriteLine(product.price);    // 123
Console.WriteLine(product.inStock); // true
  • 이렇게 하면 코드 가독성이 훨씬 좋아짐
  • 실무에서는 웬만하면 이름 붙여서 쓰는 게 기본

이런식으로 Item1,2,3은 자동으로 붙는 이름이라고 한다!

하지만 기본적인 자동으로 붙는 이름이지 우리가 정의해서 사용할수도 있다! 오...

약간 뭔가 구조체같은 느낌이다. 그래서 또 찾아봤다!

🎯 팁: 이름 붙인 튜플 vs 구조체


항목 튜플 구조체
용도 가벼운 값 묶음 더 복잡한 상태와 기능 표현
접근 (x, y) 또는 .Item1 .x, .y 필드
추천 상황 일시적 데이터 반환, 빠른 좌표 등 게임 캐릭터 정보, 상태 등

이런식이라고 한다! 음 확실히 구조체와 튜플도 명확한 차이가 있는 듯 하다.


🔸 이름 붙여서 쓰기

(string name, int age) person = ("Alice", 25);
Console.WriteLine(person.name); // Alice
Console.WriteLine(person.age);  // 25

🔸 튜플을 함수에서 반환하기

static (int sum, int count) Calculate(int[] numbers)
{
    int sum = numbers.Sum();
    int count = numbers.Length;
    return (sum, count);
}

// 사용
var result = Calculate(new int[] { 1, 2, 3 });
Console.WriteLine($"합계: {result.sum}, 개수: {result.count}");

🧠 튜플은 언제 쓰면 좋을까?

상황 이유
함수에서 여러 값을 한 번에 반환하고 싶을 때 return (x, y);
좌표처럼 값들이 묶여 있는 개념을 표현할 때 (int x, int y)
임시로 두 값을 전달하거나 비교하고 싶을 때 빠르게 사용 가능
 

🎯 요약

개념 설명
튜플 (A, B) 서로 다른 타입 여러 개를 한 덩어리로 묶음
접근 방식 .Item1, .Item2 또는 이름 지정: (int x, int y)
주 용도 간단한 데이터 묶음, 함수 복수 반환값, 좌표 표현

리스트를 사용하다 튜플이 나오길래 처음에 튜플을 보고 이게뭐야? 싶었다 그래서 궁금해서 알아봤는데 정말 유용한듯하다!

반응형