묻고 답해요
164만명의 커뮤니티!! 함께 토론해봐요.
인프런 TOP Writers
-
미해결실무에 바로 적용하는 프런트엔드 테스트 - 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가 뜹니다. 이미 파이참이 설치 되어 있어서 가상환경 외부 의존성을 설치하려고 경로를 맞추려고 하는데 해당 부분을 어디서 진행해야하는지 모르겠습니다.
-
미해결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를 써서 그냥 충분히 작은값, 큰값으로 테스트하고 넘기기)□ 아니면 리플렉션을 이용해서 상수 값을 참조해야하는지(강의 중 리플렉션에 대해 부정적으로 말씀해 주셔서, 이런 경우에도 리플렉션을 지양하는 것이 맞는지 아니면 예외적으로 고려할 수 있는 상황인지...) 강의자님의 실무 기준을 듣고싶습니다.
-
미해결Spring Boot TDD - 입문부터 실전까지 정확하게
10년간 CTO 활동을 하신 부분에 대한 질문
안녕하세요.호기심에 질문 드립니다. CTO로 여러 회사에서 활동하셨던걸로 소개를 받았는데 이중에 성공전인 회사에 입사하셔서성공 궤도에 올리신 회사가 있을까요~?
-
해결됨[매일 완독 챌린지] 저자와 함께하는 <FastAPI로 기획에서 출시까지>
4주 1회차 과제
is_host와 사용자의 캘린더 보유를 모두 사용해야 한다고 생각합니다.개인적으로 호스트가 정해진 서비스라면 모두 고려할 필요는 없지만, 실제 누구나 호스트의 역할을 하게 된다면2가지를 병행해서 사용해야 한다고 판단했습니다. 홍길동이 호스트가 될수도 있고, 게스트도 될수 있습니다.그래서 호스트별로 마스터가 될수 있는 캘린더의 아이디를 저정해서 실제 보유하고 있는 권한이 있는 호스트 인지판단하려고 합니다.
-
해결됨[매일 완독 챌린지] 저자와 함께하는 <FastAPI로 기획에서 출시까지>
4주 5회차 과제
파일삭제에 대한 정책은 있어야 한다고 생각합니다.파일 저장공간에 대한 것은 비용입니다.삭제 주기는 서비스 정책 및 신청약관에 명기해야 합니다.파일 업로드시 YYYY/MM/DD 구성으로 파일 디렉토리를 구성해서 쉽게 일괄 삭제 할수 있게 구성합니다. 부킹데이터를 삭제하면, 파일도 삭제할 것인지 여부해당 서비스가 무료/유료여부에 따라서 달라질것 같습니다.원칙은 삭제해야 하는데, 해당 서비스의 경우 파일은 바로 삭제할것 같습니다.보통 게스트의 개인정보와 관련된 것들이 많을것 같아서 바로 삭제하는 것이 좋습니다.
-
미해결Practical Testing: 실용적인 테스트 가이드
void는 어떻게 테스트하나요..? void로 애초에 코딩하면 안되나요??
강의를 수강한 이후 테스트 코드를 짜다가 문득 궁금하여 문의드립니다. 보통 테스트 코드는 입력이 있고, 그것에 대한 출력을 검증하는 것인데 반환 값이 void면 어떻게 테스트 코드를 짜면 되는지 궁금합니다. (예를들어 단순 update문) copilot이나 ai는 verify로 행위 검증을 하던데... 이렇게 하는게 맞는건지, 아니면 void로 반환하는것 자체를 지양하는게 좋은지... 의견을 여쭙고 싶습니다!!!
-
해결됨[매일 완독 챌린지] 저자와 함께하는 <FastAPI로 기획에서 출시까지>
4주 5회차 과제 제출
1) 부킹 파일 삭제 시, 먼저 '삭제 대기'로 해당하는 테이블 행에 표시를 하여 놓고, 일정 기간 후에 정리 작업을 통해 테이블로부터 삭제를 하여주고, 스토리지에서도 해당 파일을 같이 제거해 줍니다. 2) 부킹 삭제 시, 부킹을 먼저 '삭제 대기' 로 테이블 행에 표시를 하여 놓고, 파일들도 마찬 가지로 '삭제 대기'로 유지 시켜줍니다. 일정 기간 후, 부킹 데이터가 삭제 될 때, 같이 해당하는 부킹 파일 레코드 행들을 테이블로 부터 삭제하여주고, 스토리지에서도 물리적으로 제거 해줍니다. 이는 사용자가 실수로 특정 파일 및 부킹을 삭제 할 경우를 대비해 복구 할 시간을 주기 위함입니다. 즉시 물리 삭제를 수행할 경우 DB에는 삭제되었지만 파일이 남거나, 파일은 삭제되었지만 DB 에서는 계속 존재하는 불일치 문제가 발생할 수 있습니다.
-
해결됨[매일 완독 챌린지] 저자와 함께하는 <FastAPI로 기획에서 출시까지>
4주 4회차 과제 제출
게스트 예약 부킹 일정 변경 정책일자와 타임 슬롯 변경을 허용합니다.과거 일자로의 변경은 허용 되지 않습니다. 예약 날짜는 항상 현재 일자 및 시간 보다 미래 이어야 하기 때문입니다.각 호스트 고유의 등록된 타임 슬롯으로만 변경이 허용 가능 합니다. 호스트 A의 캘린더에 지정된 타임 슬롯 이외에 시간을 선택한다면, 호스트와 게스트의 만남이 불가능합니다.호스트의 캘린더에 해당 타임 슬롯의 자리가 비어있다면, 변경이 가능합니다. 그렇지 않다면, 해당 예약 내역 실패 오류를 반환합니다.성공 시나리오 호스트 타임슬롯: 화,목,금 오후 3시 - 4시, 4시 - 5시, 6시 - 7시 (1월 3일~ 1월 10일 다 비어있음) 게스트는 2026년 1월 1일에, 2026년 1월 2일 (금요일) 오후 3시 부킹을 2026년 1월 6일 (화요일) 오후 6시로 변경하려고 합니다. a) 새 부킹 날짜는 현재 날짜보다 미래 날짜이며, b) 새 부킹 날짜는 호스트의 타임 슬롯의 일자와 일치하며, c) 1월 6일 오후 6시에는 자리가 비어있으므로, 일정 변경이 성공하게 됩니다.실패 시나리오들 a) 1. 게스트는 2026년 1월 1일에, 2025년 1월 2일 (금요일) 오후 3시 부킹을 실수로 2025년 12월 30일 (화) 오후 6시로 변경하려고 합니다.새 부킹 날짜는 현재 날짜보다 과거 날짜 이므로 유효 날짜 오류를 반환해줍니다. b) 1. 게스트는 2026년 1월 1일에, 2025년 1월 5일 (금요일) 오후 3시 부킹을 2026년 1월 5일 (월) 오후 6시로 변경하려고 합니다.새 부킹 날짜는 현재 날짜보다 미래 날짜이므로 첫번째 조건을 통과합니다. 그러나, 호스트의 타임 슬롯 일자들과 맞지 않으므로, 유효하지 않은 타임슬롯 오류를 반환합니다. c) 1. 게스트는 2026년 1월 1일에, 2025년 1월 5일 (금요일) 오후 3시 부킹을 2026년 1월 6일 (화) 오후 6시로 변경하려고 합니다.새 부킹 날짜는 현재 날짜보다 미래 날짜이므로 첫 번째 조건을 통과합니다. 호스트의 타임슬롯과 일치하므로, 두 번째 조건도 통과합니다. 그러나, 호스트의 캘린더에 이미 요청 시간대에 다른 게스트와의 예약이 존재하므로, 이미 존재하는 일자 오류를 반환합니다.
-
해결됨[매일 완독 챌린지] 저자와 함께하는 <FastAPI로 기획에서 출시까지>
351쪽 질문
351쪽 내용 중에 "테스트할 때 현재 일시는 우리가 원하는 임의의 현재 일시값을 사용하도록 합니다. 종단점 함수에서는 어떻게 해야하고, conftest.py 파일에서 의존성 주입 오버라이드는 어떻게 해야할까요?"이 질문에 대한 저의 구현이 맞는지 궁금합니다. appserver.apps.calendar.deps.py appserver.apps.calendar.endpoints.py conftest.py3
-
해결됨[매일 완독 챌린지] 저자와 함께하는 <FastAPI로 기획에서 출시까지>
4주 3회차 과제
PostgreSQL의 Partial Unique Index의 기능을 활용하면 attendance_status 모델필드의 값이 cancelled 된 경우를 제외한 모든 동일 일자와 동일 타임슬롯인 경우를 중복으로 간주하는 제약을 구현 할 수 있습니다. __table_args__ = ( Index( "uq_active_booking_when_timeslot", "when", "time_slot_id", unique=True, postgresql_where=text("attendance_status <> 'CANCELLED'"), ), ) postgresql 일 경우, attendance_status 가 'CANCELLED' 가 아닌 모든 when + time_slot_id 조합에 고윳값 제약을 걸어줍니다.
-
해결됨[매일 완독 챌린지] 저자와 함께하는 <FastAPI로 기획에서 출시까지>
refresh() 메서드와 픽스처에 대해 질문이 있습니다.
픽스처 함수는 픽스처로, 픽스처 내부에서 사용되는 픽스처 함수는 객체로 표현하였습니다.=====host_user_calendar 픽스처에서 refresh() 메서드를 통해 host_user 객체를 db와 동기화해서 host_user 객체가 calendar 객체의 존재를 아는 것으로 이해했는데요.그런데 해당 host_user 객체는 반환되지 않고 host_user_calendar 픽스처에 남아있게 되어 접근할 수 없으니 무의미한 행동이라고 보여졌습니다. 그런데 refresh()를 사용하지 않으면 test_사용자가_변경하는_항목만_변경되고_나머지는_기존_값을_유지한다() 테스트 함수에서 update_calendar 엔드포인트 호출 시 404 에러가 발생하는 것을 확인했습니다. 즉, user.calendar로 접근 시 None으로 평가되는 것이지요. 그렇다면 host_user를 반환하지 않아도 해당 host_user 객체는 client_user_auth 픽스처에서 공유되는 것일까요? (혹은 동일한 객체일까요?) 그래야 refresh()를 진행했을 때 오류가 뜨지 않는다는 점이 설명이 되더라구여. 여기서 또하나 궁금한 점은 client_with_auth 픽스처보다 host_user_calendar 픽스처가 먼저 실행이 되어야 공유되는 host_user 객체가 calendar 정보를 가질 수 있다는 점이었습니다. 이 실행 순서를 결정하는 프로세스에 대해서도 궁금합니다!
-
해결됨부트캠프에서 알려주지 않는 것들 (리액트) 1편
강의 마지막이 잘려있는것 같습니다
강의 마지막이 잘려있는 것 같습니다
-
해결됨[매일 완독 챌린지] 저자와 함께하는 <FastAPI로 기획에서 출시까지>
4주 2회차 과제 질문
작가님, 4주 2회차 과제 내용이 잘 이해가 되지 않습니다 ^^;책 309쪽의 코드에서#이미 존재하는 타임슬롯과 겹치는지 확인 이 부분 에서 이미 과제에서 요구하는 사항이 구현된 것이 아닌가요?
-
해결됨[매일 완독 챌린지] 저자와 함께하는 <FastAPI로 기획에서 출시까지>
4주 1회차 과제
사용자가 캘린더를 보유했는지 여부로 확인할 것 같습니다. 이유는 추후 서비스 확장할 때 조금 더 수월할 것 같다는 생각이 들었습니다.프로젝트를 시작하면서부터 아래와 같은 생각을 했습니다.- "누구나 호스트가 되면 안될까?" - "호스트 역시도 커피챗을 신청할 수 있게 하고 싶다." 그래서 위와 같이 서비스 정책을 확장할 때를 대비해서 (마이그레이션 비용을 줄이는 방향으로) is_host로 검사하는 것이 아닌 일단 캘린더 보유 여부로 호스트인지 아닌지 확인하자라고 판단했습니다.
-
해결됨[매일 완독 챌린지] 저자와 함께하는 <FastAPI로 기획에서 출시까지>
4주 1회차 과제
두 요소를 모두 사용 하나, 각각 다른 용도로 쓸 것입니다. is_host 용도: 호스트 전용 엔드포인트 접근 통제 및 UI 메뉴 노출, 권한 기반 라우팅 호스트를 위한 endpoint API 에 대한 이용 허가 여부를 결정하는 호스트의 access token 생성 및 엔드포인트 함수 내의 조건문을 위해 필요한 모델 필드라고 생각합니다. 예상되는 우려점은 호스트 유저가 더이상 host가 되지 않을 때, host를 위한 API의 이용 허가를 거부 해야하기 때문에 is_host 필드값의 확실한 업데이트가 필요합니다. 사용자 캘린더 보유 여부 용도: 실제 예약 생성/동기화가 필요한 시점에 필수 체크, 호스트의 온보딩 플로우의 완료조건 게스트의 성공적인 Booking 을 위해서는 실제 캘린더에 대한 접근이 필요하기 때문에, 호스트는 게스트가 Booking 하기 위해서는 캘린더를 무조건 보유해야 합니다. 예상되는 우려점은 호스트의 캘린더는 호스트가 Booking을 받을 준비가 되있다면 항상 존재 해야하며, 실제 올바른 호스트에 매핑이 되어야합니다. 만약 캘린더가 존재 하지않거나, 호스트 캘린더 ID 에 대한 검증이 제대로 동작 하지 않는다면, 게스트의 Booking 이 실패로 이어지거나 다른 호스트 캘린더에 등록이 될 수 있습니다.
-
해결됨[매일 완독 챌린지] 저자와 함께하는 <FastAPI로 기획에서 출시까지>
4주 3회차 과제
중복 예약을 데이터베이스의 고유값 제약으로 예방하는 방법조건은 1)동일 일자와 동일 타임 슬롯인 경우 중복으로 간주 2)약속(부킹)이 취소되어 attendance_status 모델 필드값이 cancellled인 경우 예약 가능 위를 구현하기 위해 데이터베이스에게 아래 정보가 고유해야 한다는 특별한 규칙을 알려줄 수 있습니다.1)누가 예약하는지2)언제 예약하는지3)어느 시간대에 예약하는지 위의 세가지가 모두 같으면 중복 예약이라고 판단 합니다. 취소된 재예약은 가능하게 하기 위해고유성 규칙에 한가지 예외를 부여합니다.호스트, 날짜, 시간 슬롯의 조합은 고유해야 한다. 예외적으로 예약상태가 "취소됨"이라면 그 예약은 위의 고유성 규칙을 무시해 달라고 합니다. 새로운 예약 생성 시:데이터베이스는 예약하려는 호스트, 날짜, 시간슬롯 정보를 보고, 현재 '취소됨' 상태가 아닌 다른 예약이 이미 있는지 찾아봅니다.만약 '취소됨' 상태가 아닌 다른 예약이 이미 같은 자리에 있다면, 데이터베이스는 "안돼! 이미 예약된 자리야!"라고 하며 새로운 예약을 저장하지 못하게 합니다.만약 같은 자리에 '취소됨' 상태가 아닌 예약이 없다면, 데이터베이스는 새로운 예약을 성공적으로 저장합니다.예약 취소 시:어떤 예약이 '취소됨(cancelled)' 상태로 변경되면, 데이터베이스의 이 "특별한 규칙"은 그 취소된 예약을 더 이상 고유성 검사 대상에서 제외합니다.이제 그 자리는 "비어있는" 것처럼 간주되므로, 다른 사람이 같은 호스트, 날짜, 시간슬롯 정로 새로운 예약을 만들 수 있게 됩니다.
-
해결됨[매일 완독 챌린지] 저자와 함께하는 <FastAPI로 기획에서 출시까지>
4주 1회차 과제
사용자가 호스트인지 두 가지 방법으로 확인 가능합니다. 1. User 모델에 선언한 is_host 모델 필드가 True인지 확인 2.사용자가 캘린더를 보유했는지 확인 이 중 두 가지 방법 모두를 사용해야 한다고 생각합니다.1. User 모델에 선언한 is_host 모델 필드가 True인지 확인 -> 관점적 측면에서 명시적이고 관리적인 역할 및 자격을 부여합니다. 즉 사용자가 호스트로 자격이 있다는 것을 의미합니다. 관리자가 특정 사용자를 호스트로 지정하거나 해제하는데 사용할 수 있고 캘린더의 유무와 별개로 관리가 가능하게 서비스의 효율을 향상시켜 줍니다. 2.사용자가 캘린더를 보유했는지 확인->실제 예약 요청이 들어 왔을 때 해당 호스트가 유효한 캘린더를 보유하고 있는지 확인하는 최종적인 역할을 할 수 있습니다. 즉 위의 두 정보가 모두 필요한 이유는 존재하지 않는 사용자의 username으로 캘린더 정보를 가져오려고 하거나 호스트가 아닌 사용자의 username으로 캘린더 정보를 가져오려고 하는 시도를 막을 수 있을것으로 예상됩니다. 요약하면 is host는 "호스트로서의 신분"을, 캘린더 보유 여부는 "호스트로서의 활동을 나타내는 정보여서 모두 필요합니다.
-
해결됨[매일 완독 챌린지] 저자와 함께하는 <FastAPI로 기획에서 출시까지>
4주 3회차 과제
데이터베이스 고윳값 제약으로 예방하는 구현에 대해서 조사했습니다. 동일 일자와 동일 타임슬롯인 경우 중복Booking에 unique index를 생성하는 것인데 동일 일자 (when)과 동일 타임슬롯(time_slot_id) 조건을 생성하면 됩니다. CREATE UNIQUE INDEX uq_booking_slot ON bookings (when, time_slot_id);class Booking(SQLModel, table=True): __tablename__ = "bookings" __table_args__ = ( UniqueConstraint("when", "time_slot_id", name="uq_booking_slot"), ) attendance_status 필드값이 cancelled인 경우PostgreSQL의 경우 partial unique index가 가능합니다. where를 사용해서 field가 cancelled가 아닌 경우에 unique 제약을 걸면 됩니다. CREATE UNIQUE INDEX uq_booking_slot ON bookings (when, time_slot_id) WHERE attendance_status <> 'cancelled' ;class Booking(SQLModel, table=True): __tablename__ = "bookings" __table_args__ = ( UniqueConstraint("when", "time_slot_id", name="uq_booking_slot"), postgresql_where=text("attendance_status <> 'cancelled'") ) 다중 컬럼에 대한 unique는 종종 사용하는 방법이었는데 where를 이용해서 조건을 설정할 수 있는 건 이번에 첨 알았습니다. 필요하면 python에서 처리하곤 했었는데 데이터베이스 기능으로 이용하는게 레이스 컨디션도 피할 수 있고 장점이 있어보이네요. 실무에 적용할때도 꼭 이용해보겠습니다.