오늘의 목표

  • 아침 코드카타
  • 과제 2번 도전과제 완료
  • 과제 3번
  • 과제 4번

추가 목표:

과제 5번

 


 

1. 아침 코드카타

<문제> 콜라츠 추측

내가 작성한 코드:

 

#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>

int solution(int num) {
    int answer = 0;
    long long n = (long long)num;

    while (n != 1 && answer <= 500) {
        if (n % 2 == 0) {
            n = n / 2;
        }
        else {
            n = n * 3 + 1;
        }
        ++answer;
    }

    if (n != 1) {
        answer = -1;
    }
    return answer;
}

 

다른 사람들의 답변을 참고해 보니 더 간단하게 작성할 수 있는 방법이 있었다.

바로 삼항 연산자 사용하기.

 

간략한 코드 (삼항 연산자 사용)

#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>

int solution(int num) {
	long long answer = num;
    
    for(int i = 0; i < 500; ++i) {
    	if ( answer == 1 ) {
        	return answer;
        }
        answer = (answer % 2 == 0) ? (answer / 2) : (3 * answer + 1);
    }
    
    return -1;
}

 

[분석]

최대 연산 횟수가 정해져 있으니 for문을 사용하는 것이 더 적절한 것 같다.

만약에 조건을 충족하면 바로 인덱스값을 return해 종료시키면 되니까 더 효율적인듯.

만약 500번 연산을 완료 후 for문 밖으로 빠져나오게 된다면 -1을 반환시킨다. 

 


 

2. [3번 과제] 인벤토리 시스템 구현

 

1. 데이터 보관함 Inventory<T> 클래스 설계

 

#pragma once

template <typename T>
class Inventory
{
public:
	Inventory(int capacity = 10);
	~Inventory();
private:
	T* pItems_; // 아이템 객체 저장
	int capacity_; // 인벤토리 최대 공간
	int size_; // 저장된 아이템 개수
};

 

[주의] 

동적할당 포인터변수 T* pItems_의 메모리를 해제하는 소멸자를 반드시 정의할 것

 

[헤맨 곳]

1. inventory class를 정의한 뒤 main.cpp에서 Inventory<Item> i(3);를 통해 메모리 할당 및 선언을 하려고 했는데, 오류가 발생했다.

이는 기본 생성자가 정의되어있지 않았기 때문에 생긴 일이었다.

아까 정의한 생성자에서 디폴트 값만 추가해준다.

class Item
{
public:
	Item(std::string name = "", int price = 0);
	void PrintInfo() const;
private:
	std::string name_;
	int price_;
};

 

항상 디폴트 값을 신경쓸 것.

 

 


 

3. [4번 과제] C++ Summary - 연금술 공방 관리 시스템 구현

 

이미 존재하는 코드를 해석하고 수정하는 과제이다.

 

기존 코드 분석:

 

어떤 의도로 이 코드를 구현했을까?

연금술 레시피를 작성하고 목록에 추가하여 확인할 수 있는 시스템을 구현하려고 했다.

 

수정을 해야 한다면, 기존 코드의 수정을 최소화 할 수 있는 방안이 있을까?

지금까지 배운 내용을 토대로, 기존의 코드를 수정하지 않고 새로운 코드를 추가하여 확장하는 방식으로 작성하면 된다.

 

기존 코드는 SOLID 원칙을 잘 준수하고 있나?

각 원칙을 잘 준수하고 있는지 확인해보자.

 

 

class Potion

class PotionRecipe{
public:
	std::string potionName;
    std::vector<std::string> ingredients; 
    PotionRecipe(const std::string& name, const std::vector<std::string>& ingredients);
};

/*
분석
포션 이름과 필요 재료를 저장하는 class
*/

 

1. 단일 책임 원칙(SRP)

각 클래스는 하나의 책임만 가져야 한다.

 

-> 포션 레시피를 저장하는 기능 하나만 있으므로 ok

 

2. 개방 폐쇄 원칙(OCP)

확장에는 열려 있어야 하고, 수정에는 닫혀 있어야 한다.

 

-> 변수가 public으로 선언되어 있어 private으로 바꾸고 get/set 함수를 만들어주는 것이 좋을 것 같다.

 

3. 리스 코프 치환 원칙(LSP)

자식 클래스는 부모 클래스에서 기대되는 행동을 보장해야 한다.

 

-> 이 코드에서는 상속 관계가 없으므로 ok

 

4. 인터페이스 분리 원칙(ISP)

클라이언트는 자신이 사용하지 않는 메서드에 의존하지 않아야 한다.

역할별로 세분화된 인터페이스를 만들어야 한다.

 

-> 생성자와 변수만 존재하므로 ok

 

5. 의존 역전 원칙(DIP)

고수준 모듈은 저 수준 모듈에 직접 의존하는 것이 아니라, 두 모듈 모두 추상화에 의존해야 한다.

 

-> 코드를 그대로 올릴 수 없어서 선언문만 올려놨지만 실제 코드는 선언과 동시에 정의하고 있으므로 분리시키는 것이 좋을 것 같다.

 

 

class AlchemyWorkshop

// AlchemyWorkshop 클래스: 레시피 목록을 관리
class AlchemyWorkshop {
private:
    std::vector<PotionRecipe> recipes;

public:
    // addRecipe 메서드: 재료 목록(vector)을 매개변수로 받도록 수정
    void addRecipe(const std::string& name, const std::vector<std::string>& ingredients) {
        recipes.push_back(PotionRecipe(name, ingredients));
        std::cout << ">> 새로운 레시피 '" << name << "'이(가) 추가되었습니다." << std::endl;
    }

    // 모든 레시피 출력 메서드
    void displayAllRecipes() const {
        if (recipes.empty()) {
            std::cout << "아직 등록된 레시피가 없습니다." << std::endl;
            return;
        }

        std::cout << "\n--- [ 전체 레시피 목록 ] ---" << std::endl;
        for (size_t i = 0; i < recipes.size(); ++i) {
            std::cout << "- 물약 이름: " << recipes[i].potionName << std::endl;
            std::cout << "  > 필요 재료: ";

            // 재료 목록을 순회하며 출력
            for (size_t j = 0; j < recipes[i].ingredients.size(); ++j) {
                std::cout << recipes[i].ingredients[j];
                // 마지막 재료가 아니면 쉼표로 구분
                if (j < recipes[i].ingredients.size() - 1) {
                    std::cout << ", ";
                }
            }
            std::cout << std::endl;
        }
        std::cout << "---------------------------\n";
    }
};

 

1. 단일 책임 원칙(SRP)

각 클래스는 하나의 책임만 가져야 한다.

 

-> 레시피 목록을 추가하는 기능과 출력하는 기능이 함께 있다. 분리할 필요가 있을듯.

 

2. 개방 폐쇄 원칙(OCP)

확장에는 열려 있어야 하고, 수정에는 닫혀 있어야 한다. 

 

-> 선언과 정의를 함께 하고 있으므로 분리해줘야 한다. 헤더 파일과 cpp파일로 나누어서 구현하면 될 듯.

 

3. 리스 코프 치환 원칙(LSP)

자식 클래스는 부모 클래스에서 기대되는 행동을 보장해야 한다.

 

-> 이 코드에서는 상속 관계가 없으므로 ok

 

4. 인터페이스 분리 원칙(ISP)

클라이언트는 자신이 사용하지 않는 메서드에 의존하지 않아야 한다.

역할별로 세분화된 인터페이스를 만들어야 한다.

 

-> 앞에서 이야기했듯이 addRecipe()와 displayAllRecipes() 함수를 각각 추상 클래스로 분리해서 인터페이스를 만들면 될 듯.

 

5. 의존 역전 원칙(DIP)

고수준 모듈은 저 수준 모듈에 직접 의존하는 것이 아니라, 두 모듈 모두 추상화에 의존해야 한다.

 

-> 추상화한 클래스를 AlchemyWorkshop 클래스로 사용하면 될 것 같다.

 

주어진 코드 스니펫을 SOLID 원칙에 맞게 수정해보고 과제를 수행하자.

 

 


 

더 공부해볼 점

solid 원칙에 따라서 class와 메서드를 분리하여 구현하는 게 아직 어렵다. 익숙해질 때까지 반복해보기.

여러 클래스와 메서드가 #include를 통해 소통하고 인수를 주고받는 것을 자세히 탐구할 것.

 

 

 

'Unreal5 공부 > TIL' 카테고리의 다른 글

2026/03/26 TIL  (0) 2026.03.26
2026/03/25 TIL  (0) 2026.03.25
2026/03/23 TIL  (1) 2026.03.23
2026/03/20 TIL  (0) 2026.03.20
2026/03/19 TIL  (0) 2026.03.19

+ Recent posts