ewhk9887/korean_code_reviews_from_github huggingface dataset:
@@ -0,0 +1,60 @@
+package store.domain;
+
+import static camp.nextstep.edu.missionutils.DateTimes.now;
+
+import java.time.LocalDateTime;
+
+public enum Promotion {
+
+    NULL(""),
+    SPARKLING_BUY_TWO_GET_ONE_FREE("탄산2+1"),
+    MD_RECOMMENDATION("MD추천상품"),
+    FLASH_SALE("반짝할인");
+
+    private final String promotionName;
+    private int buyQuantity;
+    private int getQuantity;
+    private LocalDateTime startDateTime;
+    private LocalDateTime endDateTime;
+
+    Promotion(String promotionName) {
+        this.promotionName = promotionName;
+    }
+
+    static void setQuantity(Promotion promotionName, int buyQuantity, int getQuantity) {
+        promotionName.buyQuantity = buyQuantity;
+        promotionName.getQuantity = getQuantity;
+    }
+
+    static void setPromotionPeriods(Promotion promotionName, LocalDateTime startDateTime,
+                                    LocalDateTime endDateTime) {
+        promotionName.startDateTime = startDateTime;
+        promotionName.endDateTime = endDateTime;
+    }
+
+    public static boolean isPromotionValid(Promotion promotionName) {
+        LocalDateTime now = now();
+        return !now.isBefore(promotionName.startDateTime) && !now.isAfter(promotionName.endDateTime);
+    }
+
+    public String getPromotionName() {
+        return promotionName;
+    }
+
+    public int getBuyQuantity() {
+        return buyQuantity;
+    }
+
+    public int getGetQuantity() {
+        return getQuantity;
+    }
+
+    public LocalDateTime getStartDateTime() {
+        return startDateTime;
+    }
+
+    public LocalDateTime getEndDateTime() {
+        return endDateTime;
+    }
+
+}

리뷰: 저는 프로모션이 동적으로 값이 바뀔 수 있다는 조건 때문에 프로모션 값이 바뀐다면 코드를 싹 다 바꿔줘야 해서 enum에는 맞지 않다고 생각했습니다 지우님은 어떻게 생각하시나요?
@@ -0,0 +1,48 @@
+package store.domain;
+
+import static store.constants.NumberConstants.SINGLE_PRODUCT_QUANTITY;
+
+import java.util.ArrayList;
+import java.util.List;
+import store.exception.ProductNotExistException;
+
+public class StoreHouse {
+
+    private final List<Product> productList = new ArrayList<>();
+
+
+    public void buy(Product product, int quantity) {
+        product.sell(quantity);
+    }
+
+    public List<Product> findProductByName(String productName) {
+        List<Product> filteredProduct = productList.stream()
+                .filter(product -> product.getName().equals(productName))
+                .toList();
+
+        if (filteredProduct.isEmpty()) {
+            throw new ProductNotExistException();
+        }
+
+        return filteredProduct;
+    }
+
+    public boolean checkRegularPricePurchase(String productName) {
+        int count = (int) productList.stream()
+                .filter(product -> product.getName().equals(productName))
+                .count();
+        List<Product> products = findProductByName(productName);
+        Promotion promotionName = products.getFirst().getPromotionName();
+
+        return count == SINGLE_PRODUCT_QUANTITY && promotionName.equals(Promotion.NULL);
+    }
+
+    public void addProduct(Product product) {
+        productList.add(product);
+    }
+
+    public List<Product> getProductList() {
+        return productList;
+    }
+}
+

리뷰: 이름으로 product를 찾을거라면 List<Product>보다 Map<String, Product>는 어떨까요?
@@ -0,0 +1,23 @@
+package store.constants;
+
+import static store.constants.StringConstants.NEW_LINE;
+import static store.constants.StringConstants.ONE_SPACE;
+import static store.constants.StringConstants.TAP;
+
+public class OutputMessages {
+    public static String WELCOME_MESSAGE = "안녕하세요. W편의점입니다." + NEW_LINE + "현재 보유하고 있는 상품입니다." + NEW_LINE.repeat(2);
+    public static String PURCHASER_LIST_FORMAT =
+            "===========W 편의점=============" + NEW_LINE + "상품명" + TAP.repeat(2) + "수량" + TAP + "금액" + NEW_LINE;
+    public static String FREEBIE_LIST_FORMAT = "===========증" + TAP + "정=============" + NEW_LINE;
+    public static String AMOUNT_INFORMATION_FORMAT = "==============================" + NEW_LINE;
+    
+    public static String TOTAL_PURCHASE_AMOUNT = "총구매액" + TAP.repeat(2);
+    public static String PROMOTION_DISCOUNT = "행사할인" + TAP.repeat(3);
+    public static String MEMBERSHIP_DISCOUNT = "멤버십할인" + TAP.repeat(3);
+    public static String TOTAL_PRICE = "내실돈" + TAP.repeat(3) + ONE_SPACE;
+
+    public static String CURRENCY_UNIT = "원";
+    public static String QUANTITY_UNIT = "개";
+    public static String OUT_OF_STOCK = "재고 없음";
+
+}

리뷰: 오 그렇군요. 참고하겠습니다!
@@ -0,0 +1,15 @@
+package store.constants;
+
+public class StringConstants {
+    public static final String COMMA = ",";
+    public static final String DASH = "-";
+    public static final String NEW_LINE = "\n";
+    public static final String TAP = "\t";
+    public static final String OPEN_SQUARE_BRACKETS = "[";
+    public static final String CLOSE_SQUARE_BRACKETS = "]";
+    public static final String EMPTY_STRING = "";
+    public static final String ONE_SPACE = " ";
+    public static final String DATE_TIME_FORMATTER_PATTERN = "yyyy-MM-dd";
+    public static final String NUMBER_FORMAT_WITH_COMMA = "%,d";
+
+}

리뷰: 칭찬,,, 감사합니다 🥹
@@ -0,0 +1,60 @@
+package store.domain;
+
+import static camp.nextstep.edu.missionutils.DateTimes.now;
+
+import java.time.LocalDateTime;
+
+public enum Promotion {
+
+    NULL(""),
+    SPARKLING_BUY_TWO_GET_ONE_FREE("탄산2+1"),
+    MD_RECOMMENDATION("MD추천상품"),
+    FLASH_SALE("반짝할인");
+
+    private final String promotionName;
+    private int buyQuantity;
+    private int getQuantity;
+    private LocalDateTime startDateTime;
+    private LocalDateTime endDateTime;
+
+    Promotion(String promotionName) {
+        this.promotionName = promotionName;
+    }
+
+    static void setQuantity(Promotion promotionName, int buyQuantity, int getQuantity) {
+        promotionName.buyQuantity = buyQuantity;
+        promotionName.getQuantity = getQuantity;
+    }
+
+    static void setPromotionPeriods(Promotion promotionName, LocalDateTime startDateTime,
+                                    LocalDateTime endDateTime) {
+        promotionName.startDateTime = startDateTime;
+        promotionName.endDateTime = endDateTime;
+    }
+
+    public static boolean isPromotionValid(Promotion promotionName) {
+        LocalDateTime now = now();
+        return !now.isBefore(promotionName.startDateTime) && !now.isAfter(promotionName.endDateTime);
+    }
+
+    public String getPromotionName() {
+        return promotionName;
+    }
+
+    public int getBuyQuantity() {
+        return buyQuantity;
+    }
+
+    public int getGetQuantity() {
+        return getQuantity;
+    }
+
+    public LocalDateTime getStartDateTime() {
+        return startDateTime;
+    }
+
+    public LocalDateTime getEndDateTime() {
+        return endDateTime;
+    }
+
+}

리뷰: 좋은 의견 감사합니다. 
@slay1379님 리뷰에도 짧게 작성하긴 했지만 추가로 말씀드리자면
현재 null을 제외하고는 코드에서 직접 사용하는 부분이 없지만, 변동 가능성이 높기 때문에 문자열 타입으로 관리하는 편이 더 좋을 것 같습니다. (라고 작성하고 다시 생각해보니 테스트 코드가 있었네요...! ^^;; 하하)
@@ -0,0 +1,48 @@
+package store.domain;
+
+import static store.constants.NumberConstants.SINGLE_PRODUCT_QUANTITY;
+
+import java.util.ArrayList;
+import java.util.List;
+import store.exception.ProductNotExistException;
+
+public class StoreHouse {
+
+    private final List<Product> productList = new ArrayList<>();
+
+
+    public void buy(Product product, int quantity) {
+        product.sell(quantity);
+    }
+
+    public List<Product> findProductByName(String productName) {
+        List<Product> filteredProduct = productList.stream()
+                .filter(product -> product.getName().equals(productName))
+                .toList();
+
+        if (filteredProduct.isEmpty()) {
+            throw new ProductNotExistException();
+        }
+
+        return filteredProduct;
+    }
+
+    public boolean checkRegularPricePurchase(String productName) {
+        int count = (int) productList.stream()
+                .filter(product -> product.getName().equals(productName))
+                .count();
+        List<Product> products = findProductByName(productName);
+        Promotion promotionName = products.getFirst().getPromotionName();
+
+        return count == SINGLE_PRODUCT_QUANTITY && promotionName.equals(Promotion.NULL);
+    }
+
+    public void addProduct(Product product) {
+        productList.add(product);
+    }
+
+    public List<Product> getProductList() {
+        return productList;
+    }
+}
+

리뷰: 엇 그 방법도 좋을 것 같지만, 동일한 이름을 가진 상품을 모두 가져와서 각 상황에 맞게 처리하는 로직에는 List가 더 적절할 것 같습니다! 다양한 자료구조를 고민해 볼 수 있게 해주셔서 감사합니다 :)
@@ -0,0 +1,49 @@
+name: DRF Test
+
+on:
+  push:
+  workflow_call:
+
+jobs:
+  lint:
+    name: black check
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@v3
+      - uses: actions/setup-python@v4
+        with:
+          python-version: "3.11"
+      - name: install black
+        run: pip install black
+      - name: check black
+        run: black --check .
+
+  drf-test:
+    needs: lint
+    name: test drf with docker
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@v3
+
+      - name: set env file
+        run: |
+          cat <<EOF > .env
+          # DB
+          POSTGRES_DB=postgres
+          POSTGRES_USER=postgres
+          POSTGRES_PASSWORD=postgres
+          POSTGRES_PORT=5432
+          # DRF
+          DB_HOST=db
+          DJANGO_SECRET_KEY=just_test_secret_885f6d0c189dd4ccd619820b9f28f56bbe02be48d978f
+          RUN_MODE=local
+          DJANGO_ALLOWED_HOST=localhost
+          # NCP
+          NCP_ACCESS_KEY=${{ secrets.NCP_ACCESS_KEY }}
+          NCP_SECRET_KEY=${{ secrets.NCP_SECRET_KEY }}
+          EOF
+
+      - name: test
+        run: |
+          docker compose up --build -d
+          docker compose run follow-app python manage.py test

리뷰: DB관련 변수들도 secret으로 추가해주셔서 처리해주시면 더 좋을거 같습니다 :)
@@ -0,0 +1,33 @@
+from django.core.management.base import BaseCommand
+from django.contrib.auth.models import User
+from tweet.models import Follow, Post
+
+
+class Command(BaseCommand):
+    def handle(self, **options):
+        # if db has user script doesn't work
+        if User.objects.filter(username="user0").exists():
+            print("데이터가 이미 존재합니다.")
+            exit(0)
+
+        # create user data
+        for i in range(5):
+            user = User.objects.create_user(username=f"user{i}", password=f"user{i}")
+            # create post data
+            for j in range(3):
+                Post.objects.create(owner=user, content=f"{j}. post by user{i}")
+
+        # set follow data
+        for i in range(5):
+            user = User.objects.get(username=f"user{i}")
+            a = (i + 1) % 5
+            b = (i + 2) % 5
+            c = (i + 4) % 5
+            usera = User.objects.get(username=f"user{a}")
+            userb = User.objects.get(username=f"user{b}")
+            userc = User.objects.get(username=f"user{c}")
+            Follow.objects.create(user=user, follow=usera)
+            Follow.objects.create(user=user, follow=userb)
+            Follow.objects.create(user=user, follow=userc)
+
+        print("데이터 생성 완료")

리뷰: 이런방법이 있다니 많이 배우고 갑니다 감사합니다
@@ -0,0 +1,49 @@
+name: DRF Test
+
+on:
+  push:
+  workflow_call:
+
+jobs:
+  lint:
+    name: black check
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@v3
+      - uses: actions/setup-python@v4
+        with:
+          python-version: "3.11"
+      - name: install black
+        run: pip install black
+      - name: check black
+        run: black --check .
+
+  drf-test:
+    needs: lint
+    name: test drf with docker
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@v3
+
+      - name: set env file
+        run: |
+          cat <<EOF > .env
+          # DB
+          POSTGRES_DB=postgres
+          POSTGRES_USER=postgres
+          POSTGRES_PASSWORD=postgres
+          POSTGRES_PORT=5432
+          # DRF
+          DB_HOST=db
+          DJANGO_SECRET_KEY=just_test_secret_885f6d0c189dd4ccd619820b9f28f56bbe02be48d978f
+          RUN_MODE=local
+          DJANGO_ALLOWED_HOST=localhost
+          # NCP
+          NCP_ACCESS_KEY=${{ secrets.NCP_ACCESS_KEY }}
+          NCP_SECRET_KEY=${{ secrets.NCP_SECRET_KEY }}
+          EOF
+
+      - name: test
+        run: |
+          docker compose up --build -d
+          docker compose run follow-app python manage.py test

리뷰: 맞아요 다만 저 부분이 순전히 테스트만을 위한 부분이라 괜찮을 거라고 생각했어요
원래 test용 env파일을 git에 그대로 올려서 actions에서 사용했었는데 제가 실수로 ncp key를 넣어서 올리는 바람에 급하게 위와 같이 변경된 거에요.
저도 저런식으로 작성하기엔 보기 않좋아 보여서 조금 더 세련된 방식으로 변경해 보겠습니다
@@ -0,0 +1,41 @@
+import { useState, useCallback } from 'react';
+import APIError from '../../api/apiError';
+
+interface UseFetchProps<TData> {
+  queryFn: () => Promise<TData>;
+  onError?: (error: APIError) => Promise<unknown> | unknown;
+}
+
+interface UseFetchResult<TData> {
+  query: () => Promise<void>;
+  isLoading: boolean;
+  error: APIError | null;
+  data: TData;
+}
+
+export default function useFetch<TData>({
+  queryFn,
+  onError,
+}: UseFetchProps<TData>): UseFetchResult<TData> {
+  const [isLoading, setLoading] = useState<boolean>(false);
+  const [error, setError] = useState<APIError | null>(null);
+  const [data, setData] = useState<TData>({} as TData);
+
+  const query = useCallback(async () => {
+    setLoading(true);
+    setError(null);
+
+    try {
+      const data = await queryFn();
+      setData(data);
+    } catch (error) {
+      setError(error as APIError);
+
+      if (onError) onError(error as APIError);
+    } finally {
+      setLoading(false);
+    }
+  }, [queryFn, onError]);
+
+  return { query, isLoading, error, data };
+}

리뷰: hooks 디렉토리를 components 디렉토리 밖으로 이동시켜야 하지 않을까요~?
@@ -0,0 +1,80 @@
+import { useEffect, useRef, useState } from 'react';
+import { LowerArrow, UpperArrow } from './Arrows';
+import * as S from './style';
+
+interface Option {
+  content: string;
+  value: string;
+}
+
+interface DropdownProps {
+  size: S.Size;
+  options: Option[];
+  defaultContent: string;
+  onSelect: (value: string) => void;
+}
+
+const Dropdown = ({
+  size,
+  options,
+  defaultContent,
+  onSelect,
+}: DropdownProps) => {
+  const [curContent, setCurContent] = useState<string>(defaultContent);
+  const [isOpened, setIsOpened] = useState<boolean>(false);
+  const dropdownRef = useRef<HTMLDivElement>(null);
+
+  const handleDropdownClick = () => {
+    setIsOpened((prev) => !prev);
+  };
+
+  const handleOptionSelect = (option: Option) => {
+    onSelect(option.value);
+    setCurContent(option.content);
+    setIsOpened(false);
+  };
+
+  const handleClickOutside = (event: MouseEvent) => {
+    if (
+      dropdownRef.current &&
+      !dropdownRef.current.contains(event.target as Node)
+    ) {
+      setIsOpened(false);
+    }
+  };
+
+  useEffect(() => {
+    document.addEventListener('mousedown', handleClickOutside);
+    return () => {
+      document.removeEventListener('mousedown', handleClickOutside);
+    };
+  }, []);
+
+  return (
+    <S.Container ref={dropdownRef}>
+      <S.Dropdown size={size} onClick={handleDropdownClick}>
+        <S.DropdownText selectedOption={curContent}>
+          {curContent}
+        </S.DropdownText>
+        {isOpened ? <UpperArrow /> : <LowerArrow />}
+      </S.Dropdown>
+      {isOpened && (
+        <S.OptionContainer size={size}>
+          {options.map((option, index) => {
+            return (
+              <S.Option
+                key={`${option.content}_${index}`}
+                id={option.value}
+                onClick={() => handleOptionSelect(option)}
+              >
+                {option.content}
+              </S.Option>
+            );
+          })}
+        </S.OptionContainer>
+      )}
+    </S.Container>
+  );
+};
+
+export default Dropdown;

리뷰: select 태그를 사용하지 않고 직접 리스트로 만드는 정성이 대단해요 :>
@@ -0,0 +1,90 @@
+import { useEffect, useState } from 'react';
+import { SortOrder, SortType } from '../../api/types';
+import {
+  INITIAL_PAGE_NUMBER,
+  INITIAL_PAGE_SIZE,
+  PAGE_SIZE,
+} from '../../constants/paginationRules';
+import { Category, Product } from '../../types';
+import { productQueries } from './queries/product';
+
+export interface UseProductsResult {
+  products: Product[];
+  isLoading: boolean;
+  error: Error | null;
+  isLastPage: boolean;
+  page: number;
+  fetchNextPage: () => void;
+  setSort: (value: string) => void;
+  setCategory: (value: string) => void;
+}
+
+export default function useProducts(): UseProductsResult {
+  const [products, setProducts] = useState<Product[]>([]);
+  const [isLastPage, setIsLastPage] = useState(false);
+
+  const [page, setPage] = useState(INITIAL_PAGE_NUMBER);
+  const [sort, setSort] = useState<SortType>({ price: 'asc', id: 'asc' });
+  const [category, setCategory] = useState<Category | ''>('');
+
+  const {
+    query: getProducts,
+    data: productsResponse,
+    isLoading,
+    error,
+  } = productQueries.useGetProducts({
+    page,
+    category,
+    sort: Object.entries(sort).map(([field, order]) => `${field},${order}`),
+    size: page === INITIAL_PAGE_NUMBER ? INITIAL_PAGE_SIZE : PAGE_SIZE,
+  });
+
+  useEffect(() => {
+    if (!isLastPage) {
+      getProducts();
+    }
+  }, [page, sort, category, isLastPage]);
+
+  useEffect(() => {
+    setIsLastPage(false);
+    setPage(INITIAL_PAGE_NUMBER);
+  }, [category, sort]);
+
+  useEffect(() => {
+    if (productsResponse) {
+      const { last, content } = productsResponse;
+      setIsLastPage(last);
+      setProducts((prevProducts) =>
+        page === INITIAL_PAGE_NUMBER ? content : [...prevProducts, ...content]
+      );
+    }
+  }, [productsResponse]);
+
+  const fetchNextPage = () => {
+    if (isLastPage) return;
+
+    // 서버의 page size가 4로 고정되어
+    // page: 0, size: 20 요청 이후엔
+    // page: 5, size: 4 로 요청해야 함
+    setPage((prevPage) =>
+      prevPage !== INITIAL_PAGE_NUMBER
+        ? prevPage + 1
+        : prevPage + INITIAL_PAGE_SIZE / PAGE_SIZE
+    );
+  };
+
+  return {
+    products: products ?? [],
+    isLoading,
+    error,
+    page,
+    fetchNextPage,
+    setCategory: (value: string) => {
+      setCategory(value as Category);
+    },
+    setSort: (value: string) => {
+      setSort((prev) => ({ ...prev, price: value as SortOrder }));
+    },
+    isLastPage,
+  };
+}

리뷰: 리뷰 가이드에 따라 데이터 페칭 로직 부분을 조금 더 개선한다면 좋겠는데요, 
사실 저도 모르겠어서 같이 고민해보아요~~
@@ -0,0 +1,62 @@
+import React, { Component } from 'react';
+import PropTypes from 'prop-types';
+import Drawing from './Drawing';
+import '../less/Toolbox.less';
+
+class DrawContainer extends Component {
+    static propTypes = {
+        drawingData: PropTypes.array.isRequired,
+        mapLoad: PropTypes.object,
+        handleToggle: PropTypes.func.isRequired,
+        updateDrawingData: PropTypes.func.isRequired,
+        toggleLoginModal: PropTypes.func.isRequired,
+        NearByFactorItems: PropTypes.array.isRequired,
+        initDrawingListAfterSave: PropTypes.func.isRequired,
+        showDraw: PropTypes.func.isRequired,
+        showDrawingSetTitleDescriptionModal: PropTypes.func.isRequired,
+        descriptionModalShow: PropTypes.func.isRequired,
+        descriptionModalHide: PropTypes.func.isRequired
+    };
+
+    render() {
+        const {
+            drawingData,
+            mapLoad,
+            handleToggle,
+            toggleLoginModal,
+            updateDrawingData,
+            NearByFactorItems,
+            initDrawingListAfterSave,
+            showDraw,
+            showDrawingSetTitleDescriptionModal,
+            descriptionModalShow,
+            descriptionModalHide
+        } = this.props;
+        return (
+            <div id="toolbox">
+                <div id="tabMenu">
+                    <div className="eachTabMenu">그리기</div>
+                </div>
+                <div>
+                    <div>
+                        <Drawing
+                            map={mapLoad}
+                            drawingData={drawingData}
+                            updateDrawingData={updateDrawingData}
+                            toggleLoginModal={toggleLoginModal}
+                            handleToggle={handleToggle}
+                            NearByFactorItems={NearByFactorItems}
+                            initDrawingListAfterSave={initDrawingListAfterSave}
+                            showDraw={showDraw}
+                            showDrawingSetTitleDescriptionModal={showDrawingSetTitleDescriptionModal}
+                            descriptionModalShow={descriptionModalShow}
+                            descriptionModalHide={descriptionModalHide}
+                        />
+                    </div>
+                </div>
+            </div>
+        );
+    }
+}
+
+export default DrawContainer;

리뷰: less 로 빼내기
@@ -0,0 +1,62 @@
+import React, { Component } from 'react';
+import PropTypes from 'prop-types';
+import Drawing from './Drawing';
+import '../less/Toolbox.less';
+
+class DrawContainer extends Component {
+    static propTypes = {
+        drawingData: PropTypes.array.isRequired,
+        mapLoad: PropTypes.object,
+        handleToggle: PropTypes.func.isRequired,
+        updateDrawingData: PropTypes.func.isRequired,
+        toggleLoginModal: PropTypes.func.isRequired,
+        NearByFactorItems: PropTypes.array.isRequired,
+        initDrawingListAfterSave: PropTypes.func.isRequired,
+        showDraw: PropTypes.func.isRequired,
+        showDrawingSetTitleDescriptionModal: PropTypes.func.isRequired,
+        descriptionModalShow: PropTypes.func.isRequired,
+        descriptionModalHide: PropTypes.func.isRequired
+    };
+
+    render() {
+        const {
+            drawingData,
+            mapLoad,
+            handleToggle,
+            toggleLoginModal,
+            updateDrawingData,
+            NearByFactorItems,
+            initDrawingListAfterSave,
+            showDraw,
+            showDrawingSetTitleDescriptionModal,
+            descriptionModalShow,
+            descriptionModalHide
+        } = this.props;
+        return (
+            <div id="toolbox">
+                <div id="tabMenu">
+                    <div className="eachTabMenu">그리기</div>
+                </div>
+                <div>
+                    <div>
+                        <Drawing
+                            map={mapLoad}
+                            drawingData={drawingData}
+                            updateDrawingData={updateDrawingData}
+                            toggleLoginModal={toggleLoginModal}
+                            handleToggle={handleToggle}
+                            NearByFactorItems={NearByFactorItems}
+                            initDrawingListAfterSave={initDrawingListAfterSave}
+                            showDraw={showDraw}
+                            showDrawingSetTitleDescriptionModal={showDrawingSetTitleDescriptionModal}
+                            descriptionModalShow={descriptionModalShow}
+                            descriptionModalHide={descriptionModalHide}
+                        />
+                    </div>
+                </div>
+            </div>
+        );
+    }
+}
+
+export default DrawContainer;

리뷰: {``} 로 감쌀 필요가 없음