ewhk9887/korean_code_reviews_from_github huggingface dataset:
@@ -0,0 +1,16 @@
+package core.mvc.tobe;
+
+import javax.servlet.http.HttpServletRequest;
+
+public class DefaultMethodArgumentResolver implements HandlerMethodArgumentResolver {
+
+    @Override
+    public Object resolveArgument(MethodParameter methodParameter, HttpServletRequest request) {
+        return request.getParameter(methodParameter.getParameterName());
+    }
+
+    @Override
+    public boolean supportsParameter(MethodParameter methodParameter) {
+        return true;
+    }
+}

리뷰: Primitive argument지원과 HttpServletRequest argument 지원은 나누어지는게 좋을 것 같습니다!
@@ -0,0 +1,24 @@
+package core.mvc.tobe;
+
+import javax.servlet.http.HttpServletRequest;
+
+import next.model.User;
+
+public class UserMethodArgumentResolver implements HandlerMethodArgumentResolver {
+
+    @Override
+    public Object resolveArgument(MethodParameter methodParameter, HttpServletRequest request) {
+        String userId = request.getParameter("userId");
+        String password = request.getParameter("password");
+        String name = request.getParameter("name");
+        String email = request.getParameter("email");
+
+        return new User(userId, password, name, email);
+    }
+
+    @Override
+    public boolean supportsParameter(MethodParameter methodParameter) {
+        Class<?> parameterType = methodParameter.getParameterType();
+        return parameterType.equals(User.class);
+    }
+}

리뷰: 이와 같이 구현할 경우 User 객체만 사용할 수 있고 범용적으로 사용하는데는 한계가 있지 않을까?
java bean 규약을 따르는 모든 객체에 대해 자동 매핑할 수 있도록 구현해 보면 어떨까?
request로 전달되는 데이터에 해당하는 setter 메서드가 있으면 자동으로 값을 할당한다.
예를 들어 request.getParameter("userId") 값이 존재하면 setUserId()를 통해 값을 할당하는 방식으로 구현해야 java bean 규약을 따르는 객체에 범용적으로 사용할 수 있지 않을까?
@@ -0,0 +1,24 @@
+package core.mvc.tobe;
+
+import javax.servlet.http.HttpServletRequest;
+
+import next.model.User;
+
+public class UserMethodArgumentResolver implements HandlerMethodArgumentResolver {
+
+    @Override
+    public Object resolveArgument(MethodParameter methodParameter, HttpServletRequest request) {
+        String userId = request.getParameter("userId");
+        String password = request.getParameter("password");
+        String name = request.getParameter("name");
+        String email = request.getParameter("email");
+
+        return new User(userId, password, name, email);
+    }
+
+    @Override
+    public boolean supportsParameter(MethodParameter methodParameter) {
+        Class<?> parameterType = methodParameter.getParameterType();
+        return parameterType.equals(User.class);
+    }
+}

리뷰: 포비님! 말씀하신 java bean 규약에 대해서는 PrimitiveMethodArgumentResolver, WrapperMethodArgumentResolver 에서 구현하고 있는데요. 혹시 이것만으로는 부족한 것일까요?
@@ -6,6 +6,10 @@ import com.linecorp.kotlinjdsl.render.jpql.JpqlRenderer
 import com.petqua.common.domain.dto.CursorBasedPaging
 import com.petqua.common.util.createQuery
 import com.petqua.domain.member.Member
+import com.petqua.domain.order.Order
+import com.petqua.domain.order.OrderPayment
+import com.petqua.domain.order.OrderProduct
+import com.petqua.domain.product.dto.MemberProductReview
 import com.petqua.domain.product.dto.ProductReviewReadCondition
 import com.petqua.domain.product.dto.ProductReviewScoreWithCount
 import com.petqua.domain.product.dto.ProductReviewWithMemberResponse
@@ -21,7 +25,7 @@ class ProductReviewCustomRepositoryImpl(
 
     override fun findAllByCondition(
         condition: ProductReviewReadCondition,
-        paging: CursorBasedPaging
+        paging: CursorBasedPaging,
     ): List<ProductReviewWithMemberResponse> {
 
         val query = jpql(ProductReviewDynamicJpqlGenerator) {
@@ -69,4 +73,35 @@ class ProductReviewCustomRepositoryImpl(
             jpqlRenderer
         )
     }
+
+    override fun findMemberProductReviewBy(
+        memberId: Long,
+        paging: CursorBasedPaging,
+    ): List<MemberProductReview> {
+        val query = jpql(ProductReviewDynamicJpqlGenerator) {
+            selectNew<MemberProductReview>(
+                entity(ProductReview::class),
+                entity(Order::class),
+                entity(OrderPayment::class),
+            ).from(
+                entity(ProductReview::class),
+                join(Order::class).on(
+                    path(ProductReview::memberId).eq(path(Order::memberId))
+                        .and(path(ProductReview::productId).eq(path(Order::orderProduct)(OrderProduct::productId)))
+                ),
+                join(OrderPayment::class).on(path(Order::id).eq(path(OrderPayment::orderId)))
+            ).whereAnd(
+                productReviewIdLt(paging.lastViewedId),
+            ).orderBy(
+                path(ProductReview::createdAt).desc(),
+            )
+        }
+
+        return entityManager.createQuery(
+            query,
+            jpqlRenderContext,
+            jpqlRenderer,
+            paging.limit
+        )
+    }
 }

리뷰: createdAt 대신 id로 정렬해도 무방한 거 맞을까요??
id가 인덱스라 더 효율적일 것 같아서요!
@@ -1,12 +1,14 @@
 package com.petqua.application.product.review
 
+import com.petqua.application.product.dto.MemberProductReviewReadQuery
+import com.petqua.application.product.dto.MemberProductReviewResponse
+import com.petqua.application.product.dto.MemberProductReviewsResponse
 import com.petqua.application.product.dto.ProductReviewReadQuery
 import com.petqua.application.product.dto.ProductReviewResponse
 import com.petqua.application.product.dto.ProductReviewStatisticsResponse
 import com.petqua.application.product.dto.ProductReviewsResponse
 import com.petqua.application.product.dto.UpdateReviewRecommendationCommand
 import com.petqua.common.domain.findByIdOrThrow
-import com.petqua.domain.product.dto.ProductReviewWithMemberResponse
 import com.petqua.domain.product.review.ProductReview
 import com.petqua.domain.product.review.ProductReviewImage
 import com.petqua.domain.product.review.ProductReviewImageRepository
@@ -42,7 +44,8 @@ class ProductReviewService(
             query.toCondition(),
             query.toPaging(),
         )
-        val imagesByReview = getImagesByReview(reviewsByCondition)
+        val reviewIds = reviewsByCondition.map { it.id }
+        val imagesByReview = getImagesByReviewIds(reviewIds)
         val responses = reviewsByCondition.map {
             ProductReviewResponse(it, imagesByReview[it.id] ?: emptyList())
         }
@@ -59,12 +62,22 @@ class ProductReviewService(
         return ProductReviewsResponse.of(responses, query.limit)
     }
 
-    private fun getImagesByReview(reviewsByCondition: List<ProductReviewWithMemberResponse>): Map<Long, List<String>> {
-        val productReviewIds = reviewsByCondition.map { it.id }
+    private fun getImagesByReviewIds(productReviewIds: List<Long>): Map<Long, List<String>> {
         return productReviewImageRepository.findAllByProductReviewIdIn(productReviewIds).groupBy { it.productReviewId }
             .mapValues { it.value.map { image -> image.imageUrl } }
     }
 
+    fun readMemberProductReviews(query: MemberProductReviewReadQuery): MemberProductReviewsResponse {
+        val memberProductReviews = productReviewRepository.findMemberProductReviewBy(query.memberId, query.toPaging())
+        val reviewIds = memberProductReviews.map { it.reviewId }
+        val imagesByReview = getImagesByReviewIds(reviewIds)
+
+        val responses = memberProductReviews.map {
+            MemberProductReviewResponse(it, imagesByReview[it.reviewId] ?: emptyList())
+        }
+        return MemberProductReviewsResponse.of(responses, query.limit)
+    }
+
     @Transactional(readOnly = true)
     fun readReviewCountStatistics(productId: Long): ProductReviewStatisticsResponse {
         val reviewScoreWithCounts = productReviewRepository.findReviewScoresWithCount(productId)

리뷰: ids를 받게 바꾸고 재활용 넘 좋네요!!
@@ -1,5 +1,6 @@
 package com.petqua.presentation.product
 
+import com.petqua.application.product.dto.MemberProductReviewsResponse
 import com.petqua.application.product.dto.ProductReviewStatisticsResponse
 import com.petqua.application.product.dto.ProductReviewsResponse
 import com.petqua.application.product.review.ProductReviewFacadeService
@@ -9,6 +10,7 @@ import com.petqua.domain.auth.LoginMember
 import com.petqua.domain.auth.LoginMemberOrGuest
 import com.petqua.presentation.product.dto.CreateReviewRequest
 import com.petqua.presentation.product.dto.ReadAllProductReviewsRequest
+import com.petqua.presentation.product.dto.ReadMemberProductReviewsRequest
 import com.petqua.presentation.product.dto.UpdateReviewRecommendationRequest
 import io.swagger.v3.oas.annotations.Operation
 import io.swagger.v3.oas.annotations.responses.ApiResponse
@@ -63,14 +65,27 @@ class ProductReviewController(
         @PathVariable productId: Long,
     ): ResponseEntity<ProductReviewsResponse> {
         val responses = productReviewFacadeService.readAll(
-            request.toCommand(
+            request.toQuery(
                 productId = productId,
                 loginMemberOrGuest = loginMemberOrGuest,
             )
         )
         return ResponseEntity.ok(responses)
     }
 
+    @Operation(summary = "내 후기 조회 API", description = "내가 작성한 상품의 후기를 조회합니다")
+    @ApiResponse(responseCode = "200", description = "상품 후기 조건 조회 성공")
+    @SecurityRequirement(name = ACCESS_TOKEN_SECURITY_SCHEME_KEY)
+    @GetMapping("/product-reviews/me")
+    fun readMemberProductReviews(
+        @Auth loginMember: LoginMember,
+        request: ReadMemberProductReviewsRequest,
+    ): ResponseEntity<MemberProductReviewsResponse> {
+        val query = request.toQuery(loginMember)
+        val response = productReviewFacadeService.readMemberProductReviews(query)
+        return ResponseEntity.ok(response)
+    }
+
     @Operation(summary = "상품 후기 통계 조회 API", description = "상품의 후기 통계를 조회합니다")
     @ApiResponse(responseCode = "200", description = "상품 후기 통계 조회 성공")
     @GetMapping("/products/{productId}/review-statistics")

리뷰: URI 좋네용👍
@@ -10,11 +10,14 @@
     "preview": "vite preview"
   },
   "dependencies": {
+    "add": "^2.0.6",
     "react": "^18.2.0",
     "react-dom": "^18.2.0",
+    "react-hook-form": "^7.45.4",
     "react-router-dom": "^6.14.2",
     "recoil": "^0.7.7",
     "styled-components": "^6.0.7",
+    "yarn": "^1.22.19",
     "zustand": "^4.4.1"
   },
   "devDependencies": {

리뷰: yarn 패키지 설치는 실수인가용~?
@@ -0,0 +1,19 @@
+import * as S from "./styled";
+
+const Button = (props: {
+  text: string;
+  type: "button" | "submit" | "reset";
+  bgColor: "black" | "grey" | "hidden";
+  onClick?: React.MouseEventHandler<HTMLButtonElement>;
+}) => {
+  const { type, text, bgColor, onClick } = props;
+  return (
+    <>
+      <S.DefaultButton type={type} onClick={onClick} colortype={bgColor}>
+        {text}
+      </S.DefaultButton>
+    </>
+  );
+};
+
+export default Button;

리뷰: jsx를 왜 빈태그로 래핑하셨나여?
@@ -0,0 +1,19 @@
+import * as S from "./styled";
+
+const Button = (props: {
+  text: string;
+  type: "button" | "submit" | "reset";
+  bgColor: "black" | "grey" | "hidden";
+  onClick?: React.MouseEventHandler<HTMLButtonElement>;
+}) => {
+  const { type, text, bgColor, onClick } = props;
+  return (
+    <>
+      <S.DefaultButton type={type} onClick={onClick} colortype={bgColor}>
+        {text}
+      </S.DefaultButton>
+    </>
+  );
+};
+
+export default Button;

리뷰: Button Props을 지정해주실Eo

Native element attributes를 넣지않은 이유가 있을까여?
@@ -0,0 +1,76 @@
+import { styled } from "styled-components";
+
+// input
+export const RegisterInput = styled.input`
+  padding: 1rem;
+  border-radius: 8px;
+`;
+
+// label text
+export const InputLabelText = styled.div`
+  padding: 1rem 0;
+  font-weight: 600;
+  font-size: 16px;
+  color: #222;
+`;
+
+// button
+export const DefaultButton = styled.button<{
+  colortype: "black" | "grey" | "hidden";
+}>`
+  width: 100%;
+  background-color: ${(props) =>
+    props.colortype === "black"
+      ? "#222"
+      : props.colortype === "grey"
+      ? "#ddd"
+      : "#f0f0f0"};
+  color: ${(props) =>
+    props.colortype === "black"
+      ? "#fff"
+      : props.colortype === "grey"
+      ? "#222"
+      : "#cdcdcd"};
+  font-weight: 700;
+  padding: 1rem;
+  border: 0;
+  cursor: ${(props) => (props.colortype === "hidden" ? "default" : "pointer")};
+  border-radius: 8px;
+`;
+
+// titleText
+export const TitleBoldText = styled.div`
+  width: 100%;
+  padding-top: 1rem;
+  text-align: center;
+  font-size: 2rem;
+  font-weight: 600;
+  color: #222;
+`;
+
+// text contents
+export const TextContentsGreatingContainer = styled.div`
+  height: calc(100vh - 200px);
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  font-size: 24px;
+  color: #222;
+  text-align: center;
+`;
+
+export const TextContentsGreatingName = styled.div`
+  font-size: 24px;
+  font-weight: 600;
+  color: blueviolet;
+`;
+
+export const TextContentsGreatingDescription = styled.div`
+  font-size: 24px;
+`;
+
+export const GreetingNameContainer = styled.div`
+  display: flex;
+  flex-direction: row;
+`;

리뷰: Button Props 타입을 지정해주지 않은 이유가 무엇일까요?
@@ -0,0 +1,76 @@
+import { styled } from "styled-components";
+
+// input
+export const RegisterInput = styled.input`
+  padding: 1rem;
+  border-radius: 8px;
+`;
+
+// label text
+export const InputLabelText = styled.div`
+  padding: 1rem 0;
+  font-weight: 600;
+  font-size: 16px;
+  color: #222;
+`;
+
+// button
+export const DefaultButton = styled.button<{
+  colortype: "black" | "grey" | "hidden";
+}>`
+  width: 100%;
+  background-color: ${(props) =>
+    props.colortype === "black"
+      ? "#222"
+      : props.colortype === "grey"
+      ? "#ddd"
+      : "#f0f0f0"};
+  color: ${(props) =>
+    props.colortype === "black"
+      ? "#fff"
+      : props.colortype === "grey"
+      ? "#222"
+      : "#cdcdcd"};
+  font-weight: 700;
+  padding: 1rem;
+  border: 0;
+  cursor: ${(props) => (props.colortype === "hidden" ? "default" : "pointer")};
+  border-radius: 8px;
+`;
+
+// titleText
+export const TitleBoldText = styled.div`
+  width: 100%;
+  padding-top: 1rem;
+  text-align: center;
+  font-size: 2rem;
+  font-weight: 600;
+  color: #222;
+`;
+
+// text contents
+export const TextContentsGreatingContainer = styled.div`
+  height: calc(100vh - 200px);
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  font-size: 24px;
+  color: #222;
+  text-align: center;
+`;
+
+export const TextContentsGreatingName = styled.div`
+  font-size: 24px;
+  font-weight: 600;
+  color: blueviolet;
+`;
+
+export const TextContentsGreatingDescription = styled.div`
+  font-size: 24px;
+`;
+
+export const GreetingNameContainer = styled.div`
+  display: flex;
+  flex-direction: row;
+`;

리뷰: 이렇게 hex 코드가 그대로 노출되는것은 매직넘버가 아닐까 싶슴다

상수처리해주면 어떨까요?
@@ -4,19 +4,20 @@ import { Routes, Route } from "react-router-dom";
 
 // Components
 import Home from "./Home";
+import SignUp from "./SignUp";
 
 const Router = () => {
-  const [isInLogged, setisInLogged] = useState(true);
+  const [isInLogged, setIsInLogged] = useState(false);
 
   return (
     <>
       {isInLogged ? (
         <Routes>
-          <Route path="/" element={<Home />} />
+          <Route path="/" element={<Home setIsInLogged={setIsInLogged} />} />
         </Routes>
       ) : (
         <Routes>
-          <Route />
+          <Route path="/" element={<SignUp setIsInLogged={setIsInLogged} />} />
         </Routes>
       )}
     </>

리뷰: 컴포넌트는 Login 인데 InLogged는 네이밍 불일치 아닐까요?
@@ -0,0 +1,60 @@
+import {
+  UseFormRegister,
+  FieldValues,
+  UseFormWatch,
+  UseFormGetValues,
+  UseFormSetValue,
+} from "react-hook-form";
+import Button from "../atoms/Button";
+import Input from "../atoms/Input";
+import * as S from "./styled";
+
+type InputAndButtonPropsType = {
+  type: "button" | "submit" | "reset";
+  name: string;
+  onClick: () => void;
+  register: UseFormRegister<FieldValues>;
+  watch: UseFormWatch<FieldValues>;
+  getValues?: UseFormGetValues<FieldValues>;
+  setValue?: UseFormSetValue<FieldValues>;
+  isEmailPass?: boolean;
+};
+
+// 버튼과 함께 쓰는 인풋 컴포넌트
+const InputAndButton = (props: InputAndButtonPropsType) => {
+  const {
+    onClick: checkValiable,
+    type,
+    name,
+    register,
+    getValues,
+    setValue,
+    watch,
+    isEmailPass,
+  } = props;
+  return (
+    <>
+      <S.InputAndButtonContainer>
+        <Input
+          name={name}
+          register={register}
+          getValues={getValues}
+          setValue={setValue}
+          watch={watch}
+        />
+        {isEmailPass ? (
+          <Button text={"중복 확인 완료"} type={"button"} bgColor={"hidden"} />
+        ) : (
+          <Button
+            type={type}
+            text={"중복 확인하기"}
+            bgColor={"grey"}
+            onClick={checkValiable}
+          />
+        )}
+      </S.InputAndButtonContainer>
+    </>
+  );
+};
+
+export default InputAndButton;

리뷰: 인풋과 버튼을 함께 사용하는 이유가 어떤 이유에서 있었을까요? 

부모 컴포넌트에서 input과 button을 적절히 사용해주면 되는데 이렇게 분리하신 이유가 궁금함다
@@ -0,0 +1,36 @@
+import {
+  FieldValues,
+  UseFormGetValues,
+  UseFormRegister,
+  UseFormSetValue,
+  UseFormWatch,
+  useForm,
+} from "react-hook-form";
+import * as S from "./styled";
+import { useEffect } from "react";
+
+type InputPropsType = {
+  type?: string;
+  placeholder?: string;
+  name: string;
+  register: UseFormRegister<FieldValues>;
+  watch: UseFormWatch<FieldValues>;
+  getValues?: UseFormGetValues<FieldValues>;
+  setValue?: UseFormSetValue<FieldValues>;
+};
+
+const Input = (props: InputPropsType) => {
+  const { type, placeholder, name, register } = props;
+
+  return (
+    <>
+      <S.RegisterInput
+        type={type}
+        placeholder={placeholder}
+        {...register(name)}
+      ></S.RegisterInput>
+    </>
+  );
+};
+
+export default Input;

리뷰: Native input 어트리뷰트를 고려해봐도 좋을것같습니다

그리고 watch와 같은 메서드를 해당 컴포넌트에서 알아야할까요?
사용처에서 알고있어야하는 메서드일텐데 과도한 정보가 아닐까싶네요!
@@ -0,0 +1,8 @@
+import * as S from "./styled";
+
+const LabelText = (props: { children: string }) => {
+  const { children } = props;
+  return <S.InputLabelText>{children}</S.InputLabelText>;
+};
+
+export default LabelText;

리뷰: Props Type 컨벤션 지켜주세요
@@ -0,0 +1,21 @@
+import * as S from "./styled";
+
+const TextContents = () => {
+  const userName = JSON.parse(localStorage.getItem("loginData") || "").name;
+
+  return (
+    <S.TextContentsGreatingContainer>
+      <S.GreetingNameContainer>
+        <S.TextContentsGreatingName>{userName}</S.TextContentsGreatingName>
+        <S.TextContentsGreatingDescription>
+          {"님,"}
+        </S.TextContentsGreatingDescription>
+      </S.GreetingNameContainer>
+      <S.TextContentsGreatingDescription>
+        {"환영합니다."}
+      </S.TextContentsGreatingDescription>
+    </S.TextContentsGreatingContainer>
+  );
+};
+
+export default TextContents;

리뷰: 이 경우엔 TextContents가 순수한 형태라고 할수있을까여? 사용처에서 userName을 프랍스로 전달해줬다면 순수했을것같은데요!
@@ -0,0 +1,27 @@
+import Button from "../atoms/Button";
+import TextContents from "../atoms/TextContents";
+import TitleTex from "../atoms/TitleText";
+import * as S from "./styled";
+
+type MainPropsType = {
+  setIsInLogged: React.Dispatch<React.SetStateAction<boolean>>;
+};
+
+const Main = (props: MainPropsType) => {
+  return (
+    <S.Container>
+      <TitleTex>{"홈"}</TitleTex>
+      <TextContents />
+      <S.SignUpButtonContainer>
+        <Button
+          text={"로그아웃"}
+          bgColor={"black"}
+          type={"button"}
+          onClick={() => props.setIsInLogged(false)}
+        />
+      </S.SignUpButtonContainer>
+    </S.Container>
+  );
+};
+
+export default Main;

리뷰: setState를 프랍스로 전달하지않고 setState를 래핑하는 함수를 전달했으면 아래와 같이 편하게 전달이 가능하지않았을까여?
```ts
setIsInLogged: (state : boolean) => void
```
@@ -4,19 +4,20 @@ import { Routes, Route } from "react-router-dom";
 
 // Components
 import Home from "./Home";
+import SignUp from "./SignUp";
 
 const Router = () => {
-  const [isInLogged, setisInLogged] = useState(true);
+  const [isInLogged, setIsInLogged] = useState(false);
 
   return (
     <>
       {isInLogged ? (
         <Routes>
-          <Route path="/" element={<Home />} />
+          <Route path="/" element={<Home setIsInLogged={setIsInLogged} />} />
         </Routes>
       ) : (
         <Routes>
-          <Route />
+          <Route path="/" element={<SignUp setIsInLogged={setIsInLogged} />} />
         </Routes>
       )}
     </>

리뷰: 이렇게 로그인 상태여부를 Router에서 하는것이 아니라 전역으로 관리하는게 어땠을까여?

라우터 컴포넌트는 url에 매핑되는 컴포넌트를 렌더해주는 역할이라고 생각되어서여! 해당 컴포넌트가 로그인 상태여부까지 알아야했을까요?
@@ -0,0 +1,11 @@
+import SignUpForm from "../components/organisms/SignUpForm";
+
+type SignUpFormPropsType = {
+  setIsInLogged: React.Dispatch<React.SetStateAction<boolean>>;
+};
+
+const SignUp = (props: SignUpFormPropsType) => {
+  return <SignUpForm setIsInLogged={props.setIsInLogged} />;
+};
+
+export default SignUp;

리뷰: 이부분도 잘 이해가 되지않는데

Form 컴포넌트가 로그인여부를 알아야하는 이유가 없다고 생각됩니다..!

컴포넌트에게 전달되는 prop은 컴포넌트가 알아야하는 정보의 범위를 나타낸다고 봐요
@@ -0,0 +1,59 @@
+import accumulator.Accumulator;
+import accumulator.PostFixAccumulator;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.CsvSource;
+
+public class CalculatorTest {
+
+
+  @ParameterizedTest
+  @DisplayName("덧셈")
+  @CsvSource(value = {"1 2 + 3 + : 6", "10 20 + 30 + : 60",
+      "100 200 + 300 + : 600"}, delimiter = ':')
+  public void postFixAddCalculate(String expression, int expectResult) {
+    Accumulator postFixAccumulator = new PostFixAccumulator();
+    int result = postFixAccumulator.calculate(expression);
+    Assertions.assertEquals(expectResult, result);
+  }
+
+  @ParameterizedTest
+  @DisplayName("뺼셈")
+  @CsvSource(value = {"1 2 - 3 -: -4", "10 30 - 20 -: -40", "300 200 - 1 - : 99"}, delimiter = ':')
+  public void postFixMinusCalculate(String expression, int expectResult) {
+    Accumulator postFixAccumulator = new PostFixAccumulator();
+    int result = postFixAccumulator.calculate(expression);
+    Assertions.assertEquals(expectResult, result);
+  }
+
+  @ParameterizedTest
+  @DisplayName("곱셈")
+  @CsvSource(value = {"1 2 * 3 *: 6", "10 20 * 30 *: 6000",
+      "100 200 * 300 * : 6000000"}, delimiter = ':')
+  public void postFixMultiplyCalculate(String expression, int expectResult) {
+    Accumulator postFixAccumulator = new PostFixAccumulator();
+    int result = postFixAccumulator.calculate(expression);
+    Assertions.assertEquals(expectResult, result);
+  }
+
+  @ParameterizedTest
+  @DisplayName("나눗셈")
+  @CsvSource(value = {"100 10 / 1 / : 10", "10 2 / : 5", "10000 20 / 10 / : 50"}, delimiter = ':')
+  public void postFixDivideCalculate(String expression, int expectResult) {
+    Accumulator postFixAccumulator = new PostFixAccumulator();
+    int result = postFixAccumulator.calculate(expression);
+    Assertions.assertEquals(expectResult, result);
+  }
+
+  @ParameterizedTest
+  @DisplayName("사칙연산")
+  @CsvSource(value = {"5 3 2 * + 8 4 / - : 9", "7 4 * 2 / 3 + 1 - : 16",
+      "9 5 - 2 * 6 3 / +: 10"}, delimiter = ':')
+  public void PostFixCalculate(String expression, int expectResult) {
+    PostFixAccumulator calculator = new PostFixAccumulator();
+    int result = calculator.calculate(expression);
+    Assertions.assertEquals(expectResult, result);
+  }
+
+}

리뷰: 여기도 컨벤션이 쫌... 각 메서드 사이는 띄워 주시는게 원칙이고 IDE문제인건지 제가 보는 환면이 문제인건지 class 단위와 메서드 단위에 대한 블럭이 구분되지 않습니다. 이 또한 지켜주셨으면 좋겠습니다.

또한 지금 하나의 예시에 대한 테스트 코드만 작성하신 것을 볼 수 있습니다. @ParameterizedTest 를 공부해보시는게 좋을 것 같습니다.