[OOP] 음식 주문하기

2024. 4. 4. 23:52·여러가지/이것저것

음식점에서 음식을 주문하는 프로세스를 구현하기 위한 도메인 모델링 과정을 요약하면 다음과 같습니다:

1. 도메인 구성 객체 식별

  • 도메인 내 객체: 음식점의 주문 프로세스에서 중요한 역할을 하는 객체를 식별합니다. 이에는 손님, 메뉴판, 다양한 음식(예: 돈까스, 냉면, 만두), 요리사, 요리가 포함됩니다.

2. 객체 간 관계 분석

  • 손님과 메뉴판: 손님은 메뉴판을 통해 음식을 선택합니다.
  • 손님과 요리사: 손님은 요리사에게 음식을 요청(주문)합니다.
  • 요리사와 요리: 요리사는 손님의 주문에 따라 요리를 준비합니다.

3. 도메인 모델링과 추상화

  • 객체 추상화: 식별된 객체들을 정적인 타입으로 추상화하여, 각 객체의 역할과 책임을 명확히 합니다.
    • 손님 타입: 주문하는 역할을 담당합니다.
    • 요리 타입: 돈까스, 냉면, 만두 등의 구체적인 요리를 추상화합니다.
    • 메뉴판 타입: 사용 가능한 메뉴를 리스트업합니다.
    • 메뉴 타입: 개별 메뉴 항목을 나타냅니다.

4. 협력 설계

  • 객체 간 협력: 객체들이 어떻게 협력하여 목표를 달성할지 설계합니다. 예를 들어, 손님이 메뉴판에서 음식을 선택하고, 이 정보를 바탕으로 요리사가 요리를 준비하는 과정을 정의합니다.

5. 책임 할당

  • 타입별 책임: 각 객체(타입)에 적절한 책임을 할당하여, 시스템의 유연성과 확장성을 보장합니다. 예를 들어, 메뉴판은 사용자에게 선택 가능한 메뉴를 제공하는 책임을 가집니다.

6. 구현

  • 시스템 구현: 위 단계에서 정의된 객체, 관계, 책임을 바탕으로 실제 시스템을 구현합니다. 이 과정에서 프로그래밍 언어와 프레임워크 선택, 데이터베이스 설계, 인터페이스 개발 등이 포함됩니다.

이러한 과정을 통해, 음식 주문 시스템의 요구 사항을 충족시키는 효과적이고 유지보수가 용이한 소프트웨어 아키텍처를 설계하고 구현할 수 있습니다.


Java

테스트 코드

package org.example.restaurant.domain;

import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

import static org.assertj.core.api.Assertions.assertThatCode;
public class DishTest {
    @Test
    @DisplayName("Given Dish 이름과 가격이 주어졌을 때, When Dish 인스턴스를 생성하면, Then 예외가 발생하지 않는다.")
    void createTest() {
        // Given
        String name = "만두";
        int price = 5000;

        // When & Then
        assertThatCode(() -> new Dish(name, price))
                .doesNotThrowAnyException();
    }
}
package org.example.restaurant.domain;

import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;



public class MenuOptionTest {
    @Test
    @DisplayName("Given 메뉴 항목 이름과 가격이 주어졌을 때, When MenuOption 인스턴스를 생성하면, Then 예외가 발생하지 않는다.")
    void createTest() {
        // Given
        String name = "만두";
        int price = 5000;

        // When & Then
        Assertions.assertThatCode(() -> new MenuOption(name, price))
                .doesNotThrowAnyException();
    }
}
package org.example.restaurant.domain;

import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

import java.util.List;
public class MenuTest {
    @Test
    @DisplayName("Given 메뉴에 여러 메뉴 항목이 있을 때, When 특정 메뉴 항목을 선택하면, Then 해당 메뉴 항목이 반환된다.")
    void createMenuTest() {
        // Given
        Menu menu = new Menu(List.of(
                new MenuOption("돈까스", 5000),
                new MenuOption("우동", 5000),
                new MenuOption("스시", 10000)
        ));

        // When
        MenuOption selectedOption = menu.choose("돈까스");

        // Then
        Assertions.assertThat(selectedOption).isEqualTo(new MenuOption("돈까스", 5000));
    }

    @Test
    @DisplayName("Given 메뉴에 여러 메뉴 항목이 있을 때, When 특정 메뉴 항목을 선택하면, Then IllegalArgumentException 예외 객체가 반환된다.")
    void chooseTest2() {
        // Given
        Menu menu = new Menu(List.of(
                new MenuOption("돈까스", 5000),
                new MenuOption("우동", 5000),
                new MenuOption("스시", 10000)
        ));

        // When && Then
        Assertions.assertThatCode(() -> menu.choose("통닭"))
                .isInstanceOf(IllegalArgumentException.class)
                .hasMessage("해당 메뉴아이템은 없습니다.");
    }
}
package org.example.restaurant.domain;

import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

public class CookingServiceTest {
    @Test
    @DisplayName("Given 메뉴 항목이 주어졌을 때, When CookingService를 사용해서 요리를 만들면, Then 해당하는 Dish가 생성된다.")
    void makeCookTest() {
        // Given
        CookingService cookingService = new CookingService();
        MenuOption menuOption = new MenuOption("돈까스", 5000);

        // When
        Dish dish = cookingService.makeCook(menuOption);

        // Then
        Assertions.assertThat(dish).isEqualTo(new Dish("돈까스", 5000));
    }
}
package org.example.restaurant.domain;

import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

import java.util.List;


public class CustomerTest {
    @Test
    @DisplayName("Given 메뉴와 CookingService가 주어졌을 때, When Customer가 메뉴 항목을 주문하면, Then 예외가 발생하지 않는다.")
    void orderTest() {
        // Given
        Menu menu = new Menu(List.of(
                new MenuOption("돈까스", 5000),
                new MenuOption("우동", 5000),
                new MenuOption("스시", 10000)
        ));
        CookingService cookingService = new CookingService();
        Customer customer = new Customer();

        // When & Then
        Assertions.assertThatCode(() -> customer.order("스시", menu, cookingService))
                .doesNotThrowAnyException();
    }
}

구현 코드

package org.example.restaurant.domain;

/**
 * 음식 준비와 관련된 서비스를 제공하는 클래스입니다.
 */
public class CookingService {
    /**
     * 메뉴 옵션에 해당하는 요리(Dish)를 만듭니다.
     *
     * @param menuOption 메뉴 옵션
     * @return 만들어진 Dish 객체
     */
    public Dish makeCook(MenuOption menuOption) {
        Dish dish = new Dish(menuOption);
        return dish;
    }
}
package org.example.restaurant.domain;
/**
 * 음식점의 고객을 나타내는 클래스입니다.
 */
public class Customer {
    /**
     * Customer 객체 생성자.
     */
    public Customer() {
    }

    /**
     * 메뉴 이름을 사용하여 주문을 합니다. 주문된 음식은 CookingService를 통해 준비됩니다.
     *
     * @param menuName 메뉴 이름
     * @param menu 메뉴 객체
     * @param cookingService 요리 서비스
     */
    public void order(String menuName, Menu menu, CookingService cookingService) {
        MenuOption menuOption = menu.choose(menuName);
        Dish dish = cookingService.makeCook(menuOption);
    }
}
package org.example.restaurant.domain;

import java.util.Objects;

/**
 * 요리(음식)를 나타내는 클래스입니다.
 */
public class Dish {
    private final String name;
    private final int price;

    /**
     * Dish 객체 생성자.
     *
     * @param name 요리 이름
     * @param price 요리 가격
     */
    public Dish(String name, int price) {
        this.name = name;
        this.price = price;
    }

    /**
     * MenuOption 객체를 사용한 Dish 객체 생성자.
     *
     * @param menuOption 메뉴 옵션
     */
    public Dish(MenuOption menuOption) {
        this.name = menuOption.getName();
        this.price = menuOption.getPrice();
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Dish dish = (Dish) o;
        return price == dish.price && Objects.equals(name, dish.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, price);
    }
}
package org.example.restaurant.domain;

import java.util.List;
/**
 * 음식점의 메뉴를 나타내는 클래스입니다. 메뉴 옵션의 목록을 관리합니다.
 */
public class Menu {
    private final List<MenuOption> menuOptions;

    /**
     * Menu 객체 생성자.
     *
     * @param menuOptions 메뉴 옵션 목록
     */
    public Menu(List<MenuOption> menuOptions) {
        this.menuOptions = menuOptions;
    }

    /**
     * 메뉴 이름에 해당하는 메뉴 옵션을 선택합니다.
     *
     * @param name 메뉴 이름
     * @return 선택된 MenuOption 객체
     * @throws IllegalArgumentException 메뉴 이름이 목록에 없는 경우
     */
    public MenuOption choose(String name) {
        return this.menuOptions.stream()
                .filter(menuItem ->menuItem.matches(name))
                .findFirst()
                .orElseThrow(()->new IllegalArgumentException("해당 메뉴아이템은 없습니다."));
    }
}
package org.example.restaurant.domain;

import java.util.Objects;

/**
 * 메뉴 옵션을 나타내는 클래스입니다. 각 메뉴 옵션은 이름과 가격을 가집니다.
 */
public class MenuOption {
    private final String name;
    private final int price;

    /**
     * MenuOption 객체 생성자.
     *
     * @param name 메뉴 옵션 이름
     * @param price 메뉴 옵션 가격
     */
    public MenuOption(String name, int price) {
        this.name=name;
        this.price=price;
    }

    /**
     * 제공된 이름이 이 메뉴 옵션의 이름과 일치하는지 확인합니다.
     *
     * @param name 확인할 이름
     * @return 이름이 일치하면 true, 그렇지 않으면 false
     */
    public boolean matches(String name){
        return this.name.equals(name);
    }

    public String getName() {
        return name;
    }

    public int getPrice() {
        return price;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        MenuOption menuOption = (MenuOption) o;
        return price == menuOption.price && Objects.equals(name, menuOption.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, price);
    }
}

코드 설명

위의 코드는 자바로 구현된 음식 주문 시스템의 주요 컴포넌트를 나타냅니다. 시스템은 음식 주문 과정을 처리하기 위해 CookingService, Customer, Dish, Menu, MenuOption 클래스를 포함합니다. 각 클래스는 음식점 도메인의 다양한 역할과 책임을 나타내며, 이들은 상호 작용하여 사용자가 메뉴 항목을 선택하고 주문할 수 있게 합니다.

CookingService 클래스

  • CookingService는 메뉴 옵션에 기반하여 요리(Dish)를 만드는 서비스를 제공합니다. makeCook 메서드는 MenuOption 객체를 받아 해당 정보를 기반으로 새로운 Dish 객체를 생성하고 반환합니다.

Customer 클래스

  • Customer 클래스는 음식점의 고객을 나타냅니다. 고객은 order 메서드를 통해 메뉴 이름과 Menu, CookingService 객체를 사용하여 주문을 합니다. 이 메서드는 선택된 메뉴 옵션을 기반으로 요리를 준비하는 과정을 처리합니다.

Dish 클래스

  • Dish 클래스는 주문된 요리를 나타냅니다. 요리는 이름과 가격을 속성으로 가지며, MenuOption을 기반으로 새로운 Dish 인스턴스를 생성할 수 있는 또 다른 생성자도 제공합니다.

Menu 클래스

  • Menu 클래스는 메뉴 옵션의 목록을 관리하며, 메뉴 이름에 해당하는 메뉴 옵션을 선택하는 기능을 제공합니다. choose 메서드는 주어진 메뉴 이름에 일치하는 MenuOption 객체를 찾아 반환하고, 만약 해당 이름의 메뉴 옵션이 없는 경우 예외를 발생시킵니다.

MenuOption 클래스

  • MenuOption 클래스는 메뉴 옵션을 나타냅니다. 각 메뉴 옵션은 고유한 이름과 가격을 가지며, matches 메서드는 제공된 이름이 메뉴 옵션의 이름과 일치하는지 여부를 확인합니다.

테스트 코드

테스트 코드는 JUnit과 AssertJ 라이브러리를 사용하여 각 클래스의 주요 기능을 검증합니다. DishTest, MenuOptionTest, MenuTest, CookingServiceTest, CustomerTest 클래스는 각각 Dish, MenuOption, Menu, CookingService, Customer 클래스의 기능을 테스트하며, 이를 통해 주어진 조건에서 예외가 발생하지 않거나, 예상된 객체가 생성되거나, 적절한 예외가 발생하는지 등을 검증합니다.

이 코드는 객체 지향 프로그래밍 원칙을 따르며, 음식 주문 시스템의 핵심 로직을 모듈화하여 구현합니다. 각 클래스는 특정 책임을 가지며, 시스템의 다른 부분과 명확하게 상호 작용합니다.

Python

from dish_order.domain.dish import Dish
from dish_order.domain.menu_option import MenuOption


class CookingService:
    """음식 준비와 관련된 서비스를 제공한다. """

    def make_cook(self, menu_option: MenuOption) -> Dish:
        """
        제공된 메뉴 옵션을 바탕으로 요리(Dish)를 만듭니다.

        :param MenuOption menu_option: 요리를 만드는 데 사용될 메뉴 옵션
        :return: 메뉴 옵션을 바탕으로 만들어진 Dish 객체
        :rtype: Dish
        """
        return Dish.from_menu_option(menu_option)
from dish_order.domain.cooking_service import CookingService
from dish_order.domain.dish import Dish
from dish_order.domain.menu import Menu
from dish_order.domain.menu_option import MenuOption


class Customer:
    """음식점의 고객을 나타냅니다."""

    def __init__(self):
        """
        Customer 인스턴스를 초기화합니다.
        """
        pass

    def order(self, menu_name: str, menu: Menu, cooking_service: CookingService) -> None:
        """
        메뉴 이름을 사용하여 주문을 합니다. 주문된 음식은 CookingService를 통해 준비됩니다.

        :param str menu_name: 주문할 메뉴의 이름
        :param Menu menu: 메뉴 객체
        :param CookingService cooking_service: 음식 준비 서비스 객체
        """
        menu_option: MenuOption = menu.choose(menu_name)
        dish: Dish = cooking_service.make_cook(menu_option)
        print(f"Ordered dish: {dish.name} for {dish.price} price")

from dish_order.domain.menu_option import MenuOption

class Dish:
    """요리(음식)를 나타냅니다."""

    def __init__(self, name: str, price: int):
        """
        Dish 객체 생성자.

        :param str name: 요리의 이름
        :param int price: 요리의 가격
        """
        self.name = name
        self.price = price

    @classmethod
    def from_menu_option(cls, menu_option: MenuOption) -> 'Dish':
        """
        MenuOption 인스턴스를 사용하여 Dish 인스턴스를 생성합니다.

        :param MenuOption menu_option: Dish 생성에 사용될 메뉴 옵션
        :return: 생성된 Dish 객체
        :rtype: Dish
        """
        return cls(menu_option.name, menu_option.price)

    def __eq__(self, other):
        """Dish 인스턴스 간의 동등성을 비교합니다."""
        if not isinstance(other, Dish):
            return NotImplemented
        return self.name == other.name and self.price == other.price

    def __repr__(self):
        return f"Dish(name={self.name}, price={self.price})"
from dish_order.domain.menu_option import MenuOption


class Menu:
    """음식점의 메뉴를 나타냅니다."""

    def __init__(self, menu_options: list[MenuOption]):
        """
        Menu 객체 생성자.

        :param list[MenuOption] menu_options: 메뉴 옵션 객체의 리스트
        """
        self.menu_options = menu_options

    def choose(self, name: str) -> MenuOption:
        """
        메뉴 이름에 해당하는 메뉴 옵션을 선택합니다.

        :param str name: 선택하고자 하는 메뉴 옵션의 이름
        :return: 선택된 메뉴 옵션 객체
        :rtype: MenuOption
        :raises ValueError: 해당 이름의 메뉴 옵션이 없는 경우 오류를 발생시킵니다.
        """
        for option in self.menu_options:
            if option.matches(name):
                return option
        raise ValueError("해당하는 메뉴아이템은 없습니다.")

class MenuOption:
    """메뉴 옵션을 나타냅니다."""

    def __init__(self, name:str, price:int):
        """
        MenuOption 객체 생성자.

        :param str name: 메뉴 옵션의 이름
        :param int price: 메뉴 옵션의 가격
        """
        self.name = name
        self.price = price

    def matches(self, name):
        """
        제공된 이름이 이 메뉴 옵션의 이름과 일치하는지 확인합니다.

        :param str name: 확인하고자 하는 이름
        :return: 이름이 일치하면 True, 그렇지 않으면 False를 반환합니다.
        :rtype: bool
        """
        return self.name == name

    def __eq__(self, other):
        """MenuOption 인스턴스 간의 동등성을 비교합니다."""
        if not isinstance(other, MenuOption):
            return NotImplemented
        return self.name == other.name and self.price == other.price

    def __repr__(self):
        return f"MenuOption(name={self.name}, price={self.price}"

테스트 코드

from dish_order.domain.cooking_service import CookingService
from dish_order.domain.dish import Dish
from dish_order.domain.menu_option import MenuOption


def test_make_cook():
    # given
    cooking_service = CookingService()
    menu_option = MenuOption("돈까스", 5000)

    # when
    dish: Dish = cooking_service.make_cook(menu_option)

    # then
    assert dish == Dish("돈까스", 5000), "The dish should match the given menu option"
from dish_order.domain.cooking_service import CookingService
from dish_order.domain.customer import Customer
from dish_order.domain.menu import Menu
from dish_order.domain.menu_option import MenuOption


def test_order():
    # given
    menu:Menu = Menu([
        MenuOption("돈까스", 5000),
        MenuOption("탕수육", 5000),
        MenuOption("우동", 4000)
    ])
    cooking_service: CookingService = CookingService()
    customer: Customer = Customer()

    # when
    # then
    customer.order("돈까스", menu, cooking_service)
from dish_order.domain.dish import Dish

def test_dish_creation():
    # given
    name = "만두"
    price = 5000

    # when & then
    dish:Dish = Dish(name, price)
    assert dish.name == name and dish.price == price, "Dish should have the correct name and price"

from dish_order.domain.menu_option import MenuOption

def test_menu_option_creation():
    # Given
    name = "만두"
    price = 5000

    # When & Then
    menu_option: MenuOption = MenuOption(name, price)
    assert menu_option.name == name and menu_option.price == price, "MenuOption should have the correct name and price"
import pytest

from dish_order.domain.menu import Menu

from dish_order.domain.menu_option import MenuOption


def test_choose_menu_item():
    # given
    돈까스 = MenuOption("돈까스", 5000)
    menu = Menu([
        돈까스
    ])
    # when
    selected_option: MenuOption = menu.choose("돈까스")

    # then
    assert selected_option == 돈까스, "Should return the correct menu option"

def test_choose_invalid_menu_item():
    # given
    돈까스 = MenuOption("돈까스", 5000)
    menu = Menu([
        돈까스
    ])

    # when & then
    with pytest.raises(ValueError) as error:
        menu.choose("통닭")
    assert str(error.value) == '해당하는 메뉴아이템은 없습니다.', "Should raise ValueError for invalid menu item"

코드 설명

위 파이썬 코드는 음식 주문 시스템을 구현한 것으로, 각 클래스와 함수는 특정 역할을 수행하며, 파이썬의 특징을 활용해 구현되었습니다. 파이썬 코드의 특징과 핵심 요소는 다음과 같습니다:

파이썬의 특징 활용

  • 타입 힌팅(Type Hinting): 파이썬 3.5 이상부터 도입된 타입 힌트를 사용하여, 함수의 매개변수와 반환값의 타입을 명시함으로써 코드의 가독성을 높이고, 개발자가 타입 관련 오류를 미리 인지할 수 있게 도와줍니다.
  • 클래스 메서드(@classmethod): Dish 클래스에서 from_menu_option과 같이 클래스 메서드를 사용하여, 인스턴스 대신 클래스 자체에 작업을 수행하게 함으로써, 다른 객체를 인자로 받아 해당 클래스의 인스턴스를 생성하는 팩토리 메서드 패턴을 구현합니다.
  • __eq__ 매직 메서드: 객체 비교를 위해 __eq__ 메서드를 오버라이드하여, 인스턴스 간의 동등성 비교를 사용자 정의 방식으로 구현합니다. 이는 테스트 코드에서 객체 비교 시 유용하게 사용됩니다.
  • 리스트 컴프리헨션과 조건문: Menu 클래스의 choose 메서드에서는 리스트 컴프리헨션과 조건문을 활용하지 않고, for 루프와 if 문을 통해 메뉴 옵션을 검색하고 있습니다. 파이썬에서는 이러한 패턴을 더 간결하게 리스트 컴프리헨션으로 표현할 수도 있습니다.

핵심 요소

  • 명확한 에러 처리: 메뉴 이름에 해당하는 메뉴 옵션이 없을 경우 ValueError를 발생시킴으로써, 함수의 사용자에게 명확한 에러 메시지를 제공합니다. 이는 파이썬에서 일반적으로 사용되는 예외 처리 방식을 따르고 있습니다.
  • 테스트 코드의 구성: 파이썬의 assert 문을 활용하여, 각 기능의 정상 작동을 검증합니다. 이는 파이썬의 단정문을 이용한 간결한 테스트 코드 작성 방식을 보여줍니다. 또한, pytest 라이브러리를 사용한 예외 처리 테스트에서는 with 문과 함께 사용하여, 예외 상황을 명확하게 테스트하고 있습니다.
  • 객체 지향 프로그래밍(OOP)의 적용: 클래스를 이용해 도메인 모델을 구성하고, 상호작용하는 객체들 간의 관계를 통해 음식 주문 시스템의 비즈니스 로직을 구현합니다. 이는 파이썬에서 객체 지향 프로그래밍 패러다임을 어떻게 활용할 수 있는지 보여주는 좋은 예시입니다.

파이썬 코드에서 구현 시 나타나는 이러한 특징들은 파이썬의 유연성, 가독성 및 간결함을 잘 보여주며, 동시에 강력한 객체 지향 설계를 가능하게 합니다.

저작자표시 (새창열림)

'여러가지 > 이것저것' 카테고리의 다른 글

[OOP]사칙연산 계산기  (1) 2024.04.04
OOP?  (0) 2024.04.03
AMQP?  (0) 2024.04.03
MQTT?  (0) 2024.04.03
커널(Kernel)  (0) 2024.03.22
'여러가지/이것저것' 카테고리의 다른 글
  • [OOP]사칙연산 계산기
  • OOP?
  • AMQP?
  • MQTT?
hyeseong-dev
hyeseong-dev
안녕하세요. 백엔드 개발자 이혜성입니다.
  • hyeseong-dev
    어제 오늘 그리고 내일
    hyeseong-dev
  • 전체
    오늘
    어제
    • 분류 전체보기 (286)
      • 여러가지 (107)
        • 알고리즘 & 자료구조 (72)
        • 오류 (4)
        • 이것저것 (29)
        • 일기 (1)
      • 프레임워크 (39)
        • 자바 스프링 (39)
        • React Native (0)
      • 프로그래밍 언어 (38)
        • 파이썬 (30)
        • 자바 (3)
        • 스프링부트 (5)
      • 컴퓨터 구조와 운영체제 (3)
      • DB (17)
        • SQL (0)
        • Redis (17)
      • 클라우드 컴퓨팅 (2)
        • 도커 (2)
        • AWS (0)
      • 스케쥴 (65)
        • 세미나 (0)
        • 수료 (0)
        • 스터디 (24)
        • 시험 (41)
      • 트러블슈팅 (1)
      • 자격증 (0)
        • 정보처리기사 (0)
      • 재태크 (5)
        • 암호화폐 (5)
        • 기타 (0)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    OOP
    그리디
    RDS
    Spring Boot
    WebFlux
    Redis
    파이썬
    자바
    Spring WebFlux
    프로그래머스
    시험
    SAA
    취업리부트
    #개발자포트폴리오 #개발자이력서 #개발자취업 #개발자취준 #코딩테스트 #항해99 #취리코 #취업리부트코스
    java
    완전탐색
    docker
    EC2
    AWS
    DP
    reactor
    백준
    항해99
    spring
    celery
    mybatis
    FastAPI
    ecs
    Python
    Docker-compose
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.0
hyeseong-dev
[OOP] 음식 주문하기
상단으로

티스토리툴바