묻고 답해요
167만명의 커뮤니티!! 함께 토론해봐요.
인프런 TOP Writers
-
미해결QA 취업 첫걸음: 실무에서 바로 쓰는 테스트 설계
섹션 4) 8. 실무 QA의 관찰 포인트 이해 강의 질문
안녕하세요. 강의를 보던 도중 Section 4 - 8. 실무 QA의 관찰 포인트 이해에서 4분 18초부터 체크리스트 부분인데 그 부분은 안나오고 5분부터는 계속 빈 화면만 나오는데 원래 이런 내용일까요?
-
해결됨오브젝트 - 설계 원칙편
tryMove(..) 메서드 ArrayIndexOutOfBoundsException 제보
안녕하세요3-2. 조합 메서드로 리팩터링하기 의 11분 08초에 예제 코드 보면서 리팩터링을 해보고 있는데 예외가 발생했습니다. java.lang.ArrayIndexOutOfBoundsException: Index 6 out of bounds for length 6 moveNorth() 실행시 y가 -1이 입력되는데else 부분에 y를 한번 더 더해주다보니 문제가 발생한것으로 보입니다. private void moveNorth() { tryMove(0, -1); } // before private void tryMove(int incX, int incY) { if(y + incY < 0 || y + incY >= height || x + incX >= width || x + incX < 0 || roomAt(x + incX, y + incY) == null) { showBlocked(); } else { this.x += incX; this.y += y + incY; // 💩 y = -1일때 -1 + (-1)이 됨 showRoom(); } } // after private void tryMove(int incX, int incY) { if(y + incY < 0 || y + incY >= height || x + incX >= width || x + incX < 0 || roomAt(x + incX, y + incY) == null) { showBlocked(); } else { this.x += incX; this.y += incY; // ✅ showRoom(); } }
-
미해결실무에 바로 적용하는 프런트엔드 테스트 - 2부. 테스트 심화: 시각적 회귀・E2E 테스트
AvailableUsers
시각적 회귀 테스트 강의의 Storybook 코드에서 play 함수 안에 matcher를 넣지 않고 userEvent.click(...)만 사용한 이유가 궁금합니다.import { userEvent, within } from '@storybook/testing-library'; import AvailableUsers from '@/pages/login/components/AvailableUsers'; export default { component: AvailableUsers, title: '로그인/사용자 리스트', }; export const Folded = { name: '접힌 상태', }; export const Expanded = { name: '펼친 상태', play: async ({ canvasElement }) => { const canvas = within(canvasElement); await userEvent.click(canvas.getByText('⚠️ 사용 가능한 유저 리스트')); }, }; 제가 이해한 바로는, 이 스토리는 storybook에서 아코디언이 펼쳐진 시각적 상태 변화를 보여주기 위한 용도이고, 실제 기능 로직 검증은 Vitest에서 별도의 테스트 코드로 작성하는 방향이라고 생각했습니다. 1. 여기서 play 안에 matcher를 넣지 않은 것은 Storybook을 시각적 상태 확인용으로 사용하려는 의도인가요? 2. 그리고 이 컴포넌트의 동작 검증은 Storybook이 아니라 Vitest에서 담당하도록 역할을 나눈 것으로 이해하면 될까요?3. 실무에서는 보통 storybook을 어디까지 작성하는지 궁금합니다. - 물론 회사마다 다르겠지만, 강의해주시는 강사님 기준으로 의견을 듣고 싶습니다.
-
미해결실무에 바로 적용하는 프런트엔드 테스트 - 2부. 테스트 심화: 시각적 회귀・E2E 테스트
storybook/ addon react-router-dom
// package.json npm list로 확인 결과 react-router@7.13.1 storybook-addon-remix-react-router@6.1.0 react@19.2.4 storybook@10.2.17강의에 있는 내용을 토대로 typescript로 쇼핑몰 storybook 테스트를 진행 중에 React Router pannel에 로깅이 찍히지 않습니다. github, storybook docs 참고하면서 해봐도 해결이 되지를 않는데... 제 코드는 아래와 같습니다..storybook/main.ts// .storybook/main.ts import type { StorybookConfig } from "@storybook/react-vite"; const config: StorybookConfig = { stories: ["../src/**/*.mdx", "../src/**/*.stories.@(js|jsx|mjs|ts|tsx)"], addons: [ "@chromatic-com/storybook", "@storybook/addon-vitest", "@storybook/addon-a11y", "@storybook/addon-docs", "storybook-addon-remix-react-router", ], framework: "@storybook/react-vite", }; export default config; .storybook/preview.tsximport type { Preview } from "@storybook/react-vite"; import { withRouter } from "storybook-addon-remix-react-router"; const preview: Preview = { tags: ["autodocs"], parameters: { controls: { matchers: { color: /(background|color)$/i, date: /Date$/i, }, }, a11y: { // 'todo' - show a11y violations in the test UI only // 'error' - fail CI on a11y violations // 'off' - skip a11y checks entirely test: "todo", }, }, decorators: [withRouter], }; export default preview; EmptyNotice.tsximport type { MouseEvent } from "react"; import { useNavigate } from "react-router"; const EmptyNotice = () => { const navigate = useNavigate(); const handleClickBack = (event: MouseEvent<HTMLButtonElement>) => { event.preventDefault(); navigate("/"); }; return ( <div style={{ display: "flex", justifyContent: "center", height: 400, alignItems: "center", flexDirection: "column", }} > <p style={{ fontSize: "50px", fontWeight: 300 }}>텅~</p> <button onClick={handleClickBack} style={{ cursor: "pointer" }}> 홈으로 가기 </button> </div> ); }; export default EmptyNotice; EmptyNotice.stories.tsximport type { Meta, StoryObj } from "@storybook/react-vite"; import EmptyNotice from "./EmptyNotice"; const meta = { title: "장바구니/EmptyNotice", component: EmptyNotice, } satisfies Meta<typeof EmptyNotice>; export default meta; type Story = StoryObj<typeof meta>; export const Default: Story = { name: "장바구니가 빈 경우", }; AI하고 문답하면서 디버깅 하는데 좀처럼 원인을 못찾겠어서 질문올립니다.
-
미해결모든 개발자의 실무를 위한 올인원 기본기 클래스
mac python 3.10 - permission denied
기본 파이썬 명령어의 버전을 바꾸기 위해 심볼링 링크를 업데이트 하려고 하니 permission denied가 뜹니다. 이미 파이참이 설치 되어 있어서 가상환경 외부 의존성을 설치하려고 경로를 맞추려고 하는데 해당 부분을 어디서 진행해야하는지 모르겠습니다.
-
해결됨실무에 바로 적용하는 프런트엔드 테스트 - 1부. 테스트 기초: 단위・통합 테스트
mockZustand
안녕하세요.강의 너무 잘 보고 있습니다. 한 가지 궁금한 점이 있어서 QnA남깁니다.// __mocks__/zustand.js const { create: actualCreate } = await vi.importActual('zustand'); import { act } from '@testing-library/react' const storeResetFns = new Set(); export const create = createState => { const store = actualCreate(createState); const initialState = store.getState(); storeResetFns.add(()=>store.setState(initialState,true)); return store; } beforeEach(() => { act(() => storeResetFns.forEach(resetFn => resetFn())); }); 위의 __mocks__/zustand.js 에서 reset할 때 true를 사용하는 건 이해가 됩니다.그런데 아래의 mockZustandStore 에서 replace:true를 넣었는데 왜 넣어줬는지가 궁금합니다. const mockStore = (hook, state) => { const initStore = hook.getState(); hook.setState({ ...initStore, ...state }, true); }; export const mockUseUserStore = state => { mockStore(useUserStore, state); }; replace:true는 완전히 해당 store에 대한 상태,함수들을 교체 하는것으로 알고 있습니다.넣어준 의도 또는 이유가 있을까요?? 안 넣어주면 생기는 문제들이 있었기 때문에 replace:true로 한건지관례로 넣었던 건지
-
미해결실전 프론트엔드 테스트 시작하기
장바구니 테스트 코드 작성에 대한 질문입니다.
강의를 들으며 장바구니 페이지에 대해 Cypress로 테스트 코드를 작성하다가 궁금한 점이 몇가지 있어서 질문을 작성합니다 :) 장바구니 페이지에서 '장바구니의 상품을 삭제한 후 장바구니에 담겨있는 총 수량과 가격이 변경된다.'라고 테스트 시나리오를 작성했는데 이런 걸 e2e로 테스트 하는 게 맞는지 잘 모르겠습니다.이런 부분은 unit 테스트로 넘겨야 하는 부분일까요?e2e테스트는 어떤 걸 중심으로 하면 좋을지 잘 모르겠습니다. 그냥 제가 필요하다고 생각되는 게 있으면 진행을 하면 되는 걸까요? 저도 모르게 저 대신 클릭 등을 수행하고 값을 확인하는 용도로 다루게 되는 것 같습니다. 그래도 일단 1번 질문에서 작성한 테스트 시나리오에 대한 코드를 직접 작성해봤습니다.이 때, 장바구니 페이지에서 장바구니 아이템 데이터가 getServerSideProps를 통해 주입되고 있더라구요. 이 상황에서 API 모킹을 어떻게 해야할지 몰라서 Claude Sonnet 4.6이랑 구글링을 통해 몇가지 코드를 작성해봤습니다. 단순하게 테스트 코드가 작성되지 않고 다소 복잡하게 작성이 되는 것 같아서 뭔가 이 방향이 맞지 않는 것 같다는 생각이 들었습니다. 그래서 테스트 시나리오 자체를 제가 잘못 생각한 것 같다는 생각이 들기도 했습니다. SSR 환경에서 어떤 코드로 작성을 하는 게 맞는 방식인지 알 수가 없어서 한 번 확인해봐주시면 감사하겠습니다 :)a. nock + intercept 사용 - SSR은 서버 프로세스에서 진행되기 때문에 intercept 할 수 없다고 하여 nock을 사용했습니다. 코드는 해당 링크를 참고 했습니다. - 그리고 router.replace를 통해 데이터를 refetch 될 때도 백엔드 api를 intercept를 할 수 없다고 해서 '/_next/data/**/*.json'를 intercept 했습니다. - 개인적으로 아래 코드는 'nock'과 'intercept'를 혼용해서 쓰다보니 장기적으로 봤을 때 유지보수성 측면에서 좋지 않은 코드라고 느꼈습니다. it.only('장바구니의 상품을 삭제한 후 총 수량과 가격이 변경된다', () => { // 1) 초기 로드용 nock (SSR - 서버사이드 fetch) cy.task('nock', { hostname: Cypress.env('API_URL'), method: 'GET', path: '/carts', statusCode: 200, body: FIVE_CART_ITEMS, }); // 2) router.replace 후 재조회용 intercept (클라이언트사이드 fetch) cy.intercept('GET', '/_next/data/**/cart.json', { body: { pageProps: { carts: FOUR_CART_ITEMS_AFTER_DELETE }, __N_SSP: true, }, }).as('refetch'); // 삭제 API mock cy.intercept('POST', '/api/cart', { statusCode: 200, body: { data: { name: '4' } }, }).as('deleteCart'); cy.visit('/cart'); // action cy.getByCy('cart-item-delete-button').first().click(); cy.wait('@deleteCart'); cy.wait('@refetch'); // 페이지 갱신 대기 // assertion cy.contains('Unbranded Rubber Chair').should('not.exist'); cy.getByCy('cart-item').should('have.length', 4); cy.getByCy('cart-item-total-amount').should('have.text', '4'); cy.getByCy('cart-item-total-price').should('have.text', '836.00'); });b. intercept 사용그래서 초기 로드에도 intercept를 사용했습니다. it.only('장바구니의 상품을 삭제한 후 총 수량과 가격이 변경된다', () => { // 1) 초기 로드용 (SSR - 서버사이드 fetch) cy.intercept('GET', '/_next/data/**/*.json', { body: { pageProps: { carts: FIVE_CART_ITEMS }, __N_SSP: true, }, }); // 2) router.replace 후 재조회용 intercept (클라이언트사이드 fetch) cy.intercept('GET', '/_next/data/**/*.json', { body: { pageProps: { carts: FOUR_CART_ITEMS_AFTER_DELETE }, __N_SSP: true, }, }).as('refetch'); // 삭제 API mock cy.intercept('POST', '/api/cart', { statusCode: 200, body: { data: { name: '4' } }, }).as('deleteCart'); cy.visit('/cart'); // action cy.getByCy('cart-item-delete-button').first().click(); cy.wait('@deleteCart'); cy.wait('@refetch'); // 페이지 갱신 대기 // assertion cy.contains('Unbranded Rubber Chair').should('not.exist'); cy.getByCy('cart-item').should('have.length', 4); cy.getByCy('cart-item-total-amount').should('have.text', '4'); cy.getByCy('cart-item-total-price').should('have.text', '836.00'); });c. API 모킹을 사용하지 않기 - ssr 환경에서 delete 테스트를 위해 api 모킹을 사용하는 게 오히려 복잡하게 느껴져서 API 모킹을 사용하지 않고 코드를 작성하는 방식을 생각했습니다. - 대신에 테스트 할 때마다 수량과 가격이 변경될 거라고 생각해서 수량과 가격을 캡쳐하는 코드를 추가했습니다. it.only('장바구니의 상품을 추가한 뒤 삭제하면 해당 상품이 사라진다', () => { const cartItem = { "id": "24", "name": "Practical Frozen Pants", "price": "607.00", "imageUrl": "https://cdn.pixabay.com/user/2023/05/21/19-38-51-804_250x250.jpg" } // prepare: API로 상품 직접 추가 cy.request('POST', `${Cypress.env('API_URL')}/carts`, cartItem).then(({ body }) => { cy.visit('/cart'); // 삭제 전 수량과 가격 캡처 cy.getByCy('cart-item-total-amount').invoke('text').then((beforeAmount) => { cy.getByCy('cart-item-total-price').invoke('text').then((beforePrice) => { // action: 방금 추가한 항목 삭제 cy.contains('Practical Frozen Pants') .closest('[data-cy="cart-item"]') .find('[data-cy="cart-item-delete-button"]') .click(); // assertion cy.contains('Practical Frozen Pants').should('not.exist'); cy.getByCy('cart-item-total-amount').should('have.text', String(Number(beforeAmount) - 1)); cy.getByCy('cart-item-total-price').should('have.text', (Number(beforePrice) - cartItem.price).toFixed(2)); }); }); }); }); 일반적으로 delete에 대해 테스트할 때 어떤 식으로 하는지 궁금합니다. 삭제 성공 여부 / 해당 데이터가 보이지 않는지 체크 정도만 하고 삭제가 진행됨으로써 총 아이템 개수 수량이 변경되는 것에 대해서는 테스트 코드로 굳이 짚고 넘어가지 않나요? 실무에서 테스트 데이터는 보통 어떻게 관리하나요? 전부 api 모킹으로 테스트 해야하는 건지, 아니면 테스트용 DB를 따로 생성해서 거기서 테스트를 진행해야하는 건지 궁금합니다.아니면 상황에 따라서 api 모킹을 해야할 때가 있고, DB를 연결해서 테스트 해야할 때가 있나요?한번에 질문이 다소 많아서 죄송합니다 ㅠㅠ 완강하고 며칠동안 혼자서 공부해보다가 제대로 제가 하고 있는 건지 모르겠어서 여쭤봅니다 ㅠㅠ 긴 글 읽어주셔서 감사합니다!!
-
미해결Practical Testing: 실용적인 테스트 가이드
private 상수 테스트 관련 질문
안녕하세요, 선생님의 강의 덕에 개인 프로젝트에 테스트를 적용하는 재미를 느끼고있는 수강생입니다. 강의 내용을 참고하여 개인 프로젝트에서 도메인 테스트를 작성하던 중,한 가지 개념적으로 헷갈리는 지점이 있어 질문드립니다. 강의 중“private 메서드는 테스트할 필요도 없고, 해서도 안 된다”라고 말씀 주셨는데, 엔티티 내부에 도메인 정책으로서 private static final 상수와 이를 사용하는 private 검증 로직이 존재하는 경우엔 어떻게 하지...? 라는 궁금증이 생겼습니다.이때 테스트에서는 경계값을 검증해야 하는데,@Entity public class TransactionFile extends BaseEntity { private static final long MAX_FILE_SIZE = 10; // 중략 private static void validateSize(long size) { state(size != 0, "파일 크기는 0보다 커야합니다."); state(size <= MAX_FILE_SIZE*1024*1024, "파일 크기는" + MAX_FILE_SIZE + "MB 이하여야 합니다."); } } 상수가 private 이므로 테스트 코드에서 값을 참조할 수 없습니다.class TransactionFileTest { long fileSize = MAX_FILE_SIZE * 1024 * 1024 - 1; // 테스트에서는 접근 불가 } 결국 궁금한 점은 private 상수를 경계값 테스트 기준으로 사용할 시...□ 단순히 도메인 모델 정책만 지켜지는지 검증하고 경계값 테스트를 안하는게 맞는지 (예 - MIN_VALUE, MAX_VALUE를 써서 그냥 충분히 작은값, 큰값으로 테스트하고 넘기기)□ 아니면 리플렉션을 이용해서 상수 값을 참조해야하는지(강의 중 리플렉션에 대해 부정적으로 말씀해 주셔서, 이런 경우에도 리플렉션을 지양하는 것이 맞는지 아니면 예외적으로 고려할 수 있는 상황인지...) 강의자님의 실무 기준을 듣고싶습니다.
-
미해결실무에 바로 적용하는 프런트엔드 테스트 - 1부. 테스트 기초: 단위・통합 테스트
프로젝트 세팅 오류 및 버전 오류 문제 문의
안녕하세요 강의를 보며 프로젝트 세팅을 하면서 궁금한 점이 있어 여쭤봅니다.예제 프로젝트와 강의가 업데이트 될 예정은 없을까요?전체적인 Dependencie들이 옛날 버전이라 예제 프로젝트들에서 익스텐션 호환 및 에러들이 너무 많습니다 ㅠㅠ..1-3강 예시 프로젝트 소개 같은 경우는 ESLint 및 서버 오류들이 너무 많아 해결 못하고 넘어간 상황입니다.2-1강 같은 경우도 Windows/Mac OS 차이(LF, CRLF)로 발생하는 줄바꿈 문자 차이 에러가 나오고 영상에 나온 익스텐션에 경우 Vitest Version >= v1.4.0이라 vitest 및 vitest/ui 등 업데이트를 매번 해야됩니다. (강의는 v0.33.0)2-1까지만 수강하더라도 전체 강의 5%를 넘어 환불도 안되는 상황이라 매번 에러 찾고 수정하는 실습은 제외하고 테스팅 흐름만 파악하는 용도로 강의를 들으려고 합니다.업데이트 예정이 있다면 알려주시면 감사하겠습니다!
-
미해결Practical Testing: 실용적인 테스트 가이드
void는 어떻게 테스트하나요..? void로 애초에 코딩하면 안되나요??
강의를 수강한 이후 테스트 코드를 짜다가 문득 궁금하여 문의드립니다. 보통 테스트 코드는 입력이 있고, 그것에 대한 출력을 검증하는 것인데 반환 값이 void면 어떻게 테스트 코드를 짜면 되는지 궁금합니다. (예를들어 단순 update문) copilot이나 ai는 verify로 행위 검증을 하던데... 이렇게 하는게 맞는건지, 아니면 void로 반환하는것 자체를 지양하는게 좋은지... 의견을 여쭙고 싶습니다!!!
-
해결됨실무에 바로 적용하는 프런트엔드 테스트 - 1부. 테스트 기초: 단위・통합 테스트
toggleIsModalOpened 테스트 할때 궁금한 점이 있습니다.
제 생각에는 단순히 true가 됨을 테스트 할 것이 아닌 false => true => false와 같은 테스트 단계가 필요하다는 생각이 드는데 이런 연속적인 테스트는 어떤 식으로 구성하는 것이 좋은가요?
-
미해결Playwright 기초 - 기초적인 활용법과 핵심 원리
e2e 폴더 내에 파일을 생성하는 이유가 뭔가요?
그냥 블로그 같은 가이드 보고 폴더를 막 생성했더니 폴더가 어디에 접속해야 있는지도 모르겠고, e2e 폴더 같은건 전혀 없는데요, e2e 폴더 내에 파일을 생성해야하는 이유가 뭔가요?꼭 폴더를 강사님이 세팅한대로 설정해야하나요?처음 폴더 설정이 finder (폴더)로 보여져서 생성되지 않아, 테스트파일 생성 위치와 구조를 처음에 어떻게 잡아야 할지 모르겠습니다.
-
해결됨부트캠프에서 알려주지 않는 것들 (리액트) 1편
강의 마지막이 잘려있는것 같습니다
강의 마지막이 잘려있는 것 같습니다
-
미해결ISTQB AI Testing - 베스트 프랙티스를 배우고 시험에 대비
27강 동영상 재생 불량
27강 재생이 중간에 끊깁니다. 27강 끝까지 못 들어서 완료율이 100% 안되는데 확인 부탁드립니다.
-
해결됨오브젝트 - 설계 원칙편
5-4 Sealed Interface는 주로 모든 케이스 검증이 필요할 때 사용하나요?
안녕하세요 좋은 강의 너무 잘 듣고 있습니다. 강의 내용중 parseCommand 메서드를 분리하는 과정에서 sealed interface 를 사용하셨는데 sealed interface 를 쓴 목적이 컴파일타임에 모든 가능한 명령어 케이스를 처리했는지 검증하기 위함인가요? sealed interface 는 주로 모든 케이스를 검증하고 싶을 때 사용한다고 판단하면 될까요?
-
미해결실무에 바로 적용하는 프런트엔드 테스트 - 1부. 테스트 기초: 단위・통합 테스트
로딩/에러처리 검증은 어떻게 하는게 좋을까요?
안녕하세요. API 요청이 포함된 컴포넌트를 대상으로 단위/통합 테스트할때 요청 상태에 따라 로딩, 에러 UI가 적절하게 렌더링 되는지도 검증을 해야하는지 궁금합니다.해야한다면, API 호출하는 모든 테스트에서 로딩/에러 UI 검증을 하는게 일반적이고 맞는 건지, 아니면 제 경우에는 ErrorBoundary랑 Suspense를 다음처럼 합쳐서 에러/로딩 처리용 wrapper(?)를 만들었는데, 이런 wrapper 테스트를 따로 작성하는게 맞는건지.. 궁금합니다 ㅎㅎ..interface ComposedBoundaryProps extends ErrorBoundaryPropsWithRender { suspenseFallback: ReactNode; children: ReactNode; reset: () => void; } const ComposedBoundary = ({ suspenseFallback, children, fallbackRender, reset, }: ComposedBoundaryProps) => { return ( <ErrorBoundary onError={(error: unknown) => { // ... }} onReset={reset} fallbackRender={fallbackRender} > <Suspense fallback={suspenseFallback}>{children}</Suspense> </ErrorBoundary> ); };
-
미해결실무에 바로 적용하는 프런트엔드 테스트 - 1부. 테스트 기초: 단위・통합 테스트
통합 테스트에서 API 요청에 대한 검증은 이루어지지 않아도 괜찮을까요?
FE에서 모킹을 포함한 통합 테스트를 진행할 때, API 요청 전까지의 동작API 요청API 응답API 응답에 따른 화면 변환 1, 4는 테스트에서 주로 다루고 있고,3은 모킹으로 처리하고 있는데, 2번의 경우, 예를 들어 화면에서 필터로 { name: "apple", limit: 10 } 를 설정하고 요청이 발생했으면, request의 parameter에 이런 요청이 도달했다는 것 까지 검증하면 좋을지가 궁금합니다. 생성/삭제/수정과 같은 API가 포함된 경우 동일 GET API라도 다른 응답이 반환되어야 되는 경우가 있어, 해당 테스트 시 응답 자체를 override해서 모킹하여서 작성하였는데, 이 경우 사실상 API 응답과 API 응답에 따른 화면 변환은 테스트가 성공하는 채로 고정하는데, 실제 요청이 올바르게 이루어졌는지는 검증하지 못하는 것 같아서요.
-
미해결ISTQB AI Testing - 베스트 프랙티스를 배우고 시험에 대비
실러버스 제공 여부
강의자료와 실러버스는 제공되지 않는 건가요?
-
미해결따라하며 배우는 리액트 A-Z[19버전 반영]
강의 소스 코드 압축 풀기 오류
강의 소스 코드를 다운로드 받아 압축 풀기를 하는데 99% 끝부분에서 특정 파일이 에러가 발생해 넘어가지지를 않습니다.건너띄기로 100% 완료하기는 했지만 찜찜해서 문의 드립니다.
-
미해결Java/Spring 테스트를 추가하고 싶은 개발자들의 오답노트
service.port 패키지로 이동한 UserRepository가 infrastructure에 있는 UserEntity에 의존
16강 '외부 연동을 다루는 방법'의 04:15에 보면,service.port 패키지로 이동한 UserRepository가 infrastructure에 있는 UserEntity에 의존하고 있는 상태입니다.아마 후속 강의에서 UserEntity에 대한 의존도 해소될 것으로 예상되지만, 여기에서도 언급은 해주시면 더 좋을 것 같습니다.