💸딱 하루, 인프런 천원샵 오픈!

블로그

제이든(양준식)

인프런 비즈니스 신규 기능 업데이트

인프런 비즈니스 신규 기능 업데이트 소식을 전합니다. 지난해는 글로벌 서비스와 앱 서비스 출시를 중심으로 발전해온 한 해였습니다. 올해는 관리자분들의 운영 부담을 덜고, 수강생들의 학습 효과를 높이는 다양한 기능들을 선보일 예정입니다. 앞으로 더욱 자주 소식을 전해드릴 예정이니 많은 관심과 응원 부탁드립니다 :) INDEX01 - 인프런 비즈니스 승인 예약 기능 출시02 - 인프런 APP 출시03 - 인프런 글로벌 서비스 출시 01 — 인프런 비즈니스 승인 예약 기능 출시 ⏳'승인 예약' 기능이 새롭게 추가되었습니다! 이제 수강 신청 내역을 원하는 날짜와 시간에 맞춰 자동으로 승인할 수 있습니다. 기존에는 교육 시작일에 맞춰 직접 관리자 페이지에 접속해 수강 신청을 수동으로 승인해야 했지만, 이제는 미리 승인 일정을 설정하면 지정한 시간에 자동으로 승인 처리가 이루어집니다. 관리자의 운영 부담을 줄이고, 보다 효율적인 학습 환경을 만들 수 있도록 승인 예약 기능을 활용해보세요.  📌 CASE 01 - 지정된 수강 신청 기간이 있는 경우 (매월 N일 ~ N일)수강 신청 기간 : 학습자는 지정된 기간동안 자유롭게 수강 신청할 수 있습니다.관리자 승인 처리 : 수강 신청 기간 종료 후, 관리자가 신청 내역을 검토하여 승인 또는 반려 처리합니다.승인 예약 설정 : 관리자는 교육 시작일(OO월 OO일 OO시)에 맞추어 승인 예약을 설정합니다.자동 승인 처리 : 설정된 시간에 반려 건을 제외한 모든 수강 신청이 자동으로 일괄 승인됩니다. 📌 CASE 02 - 수강 신청 기간이 지정되어 있지 않은 경우 (매월 1일 ~ 말일)수강 신청 : 학습자들은 별도의 기한 없이 자유롭게 수강 신청할 수 있습니다.승인 예약 설정 : 관리자는 교육 시작일(OO월 OO일 OO시)에 맞춰 승인 예약을 설정합니다.자동 승인 처리 : 설정된 시간에 반려 건을 제외한 모든 수강 신청이 일괄 승인됩니다.📌 Tip - 매월 1일 교육이 시작되는 경우, 1일 자정(00:00)에 승인 예약 기능을 활용하시면 편리합니다. 02 — 인프런 APP 출시 📱인프런 모바일 앱, 사용해보셨나요? 이제는 노트북 없이도 출퇴근길, 카페, 이동 중 언제 어디서나 편리하게 강의를 들을 수 있습니다. 앞으로도 지속적인 기능 업데이트를 통해 더욱 완벽한 학습 도구로 발전해 나갈 예정이니, 많은 관심과 이용 부탁드립니다! 📌 POINT 01 - 인프런 모바일 앱 다운로드iOS와 Android 앱 모두 사용할 수 있어요.24년도 6월 이전에 다운로드 받았던 인프런 앱은 삭제하고, 신규 출시된 앱을 다운로드 받아주세요. 📌 POINT 02 - 오프라인 재생 기능 지원강의를 다운로드하여 오프라인에서도 시청할 수 있습니다.이동 중에도 데이터 걱정 없이 강의를 편리하게 학습하세요. 📌 POINT 02 - 강의실 제스처 지원IT 강의 특성상 코드 화면을 볼 일이 많죠? 많은 개발자분들이 요청하셨던 제스처 기능이 추가되었습니다. 롱프레스 → 2배속 재생더블탭 → 5초 건너뛰기제스처 → 전체 화면 전환줌 인/아웃 기능 → 코드 화면 확대 및 축소 가능 수강예정, 수강 중 강의와 완강한 강의를 구분해서 쉽게 확인할 수 있어요. 캡처를 이용해서 원하는 수업 구간을 사진 보관함에 저장할 수 있어요.개인 스크린샷, 영상녹화는 불가하니 이 점 유의해주세요. 영상뿐만 아니라 준비해 둔 수업자료도 놓치지 마세요.전체화면인 경우에는 플레이어 하단에 수업자료 버튼을 클릭해 보세요. 전체화면이 아닐 때 플레이어를 하단으로 쓸어보세요. 내 학습 리스트로 바로 갈 수 있어요. 플레이어 영역을 반복 터치해 보세요 *초 이동으로 원하는 시점에서 수강할 수 있어요. 03 — 인프런 글로벌 서비스 출시 🌏이제 인프런이 국경을 넘어 더 많은 학습자들과 함께합니다! 영어, 베트남어, 일본어 버전이 정식 출시되었으며, 모든 강의에서 다국어 자막과 더빙을 지원합니다. 원하는 언어로 강의를 학습할 수 있어, 더욱 자유롭고 편리한 학습 경험을 제공합니다. 인프런이 추구하는 "Learn, Share, Grow"의 가치가 어떻게 글로벌로 확장되는지, 함께 지켜봐 주세요! 📌 글로벌 학습 환경을 위한 핵심 기능다국어 자막 & 더빙 지원 – 한국어 강의도 영어, 일본어, 중국어 등 다양한 언어로 시청 가능글로벌 학습자 대상 콘텐츠 제공 – 해외 학습자도 국내 인기 강의를 모국어로 학습 가능📌 기업 맞춤형 글로벌 교육 지원해외 지사 직원 교육이나 국내 외국인 직원을 위한강의 콘텐츠가 필요한 기업은 편하게 문의 주세요.문의 | group@inflab.com📌 인프런 글로벌 서비스 둘러보기English - https://www.inflearn.com/enTiếng Việt - https://www.inflearn.com/vi日本語 - https://www.inflearn.com/ja 직원이 성장하고, 조직이 성공하는 인프런 비즈니스🔗 인프런 비즈니스 홈페이지🔗 인프런 비즈니스 콘텐츠  우리는 성장 기회의 평등을 추구합니다.©인프랩 | 경기도 성남시 분당구 판교로289번길 20, 3동 5F©InfLab. All rights reserved.

인프런비즈니스

제이든(양준식)

인프런 비즈니스 도입 검토 시 가장 자주 묻는 질문 Top 8

인프런 비즈니스의 특징은, 이미 많은 임직원이 인프런에서 학습한 경험을 바탕으로 기업 도입을 요청하는 경우가 많다는 점입니다. 하지만 정작 비즈니스 서비스는 직접 사용해본 경험이 없다 보니, 교육 담당자들의 문의가 이어집니다. 인프런 비즈니스 도입을 고민하는 기업 교육 담당자들이 가장 궁금해하는 질문 TOP 8, 지금 바로 확인해보세요! INDEX01. 비용은 어떻게 되나요?02. 결제는 어떻게 진행되나요?03. LMS 기능이 제공되나요?04. 원하는 강의만 지원할 수 있나요?05. 강의는 어떻게 구성되어 있나요?06. 수강 신청은 어떻게 이뤄지나요?07. 계정 관리는 어떻게 하나요?08. 수료증 발급은 가능한가요? 01. 비용은 어떻게 되나요?인프런 비즈니스는 매달 고정 비용이 청구되는 방식이 아니라, 수강 신청한 강의에 대해서만 비용이 발생하는 구조입니다. 학습자가 필요한 강의를 원하는 시점에 신청하고, 실제 수강한 강의에 대해서만 비용을 부담하기 때문에 불필요한 예산 지출을 최소화할 수 있습니다.비용은 월별로 정산되며, 해당 기간 동안 임직원들이 수강 신청한 강의 수에 따라 책정됩니다. 강의 금액 × 수강 신청 인원수 방식으로 계산되며, 기업은 실제 수강한 강의에 대해서만 결제하면 됩니다. 덕분에 예산을 효율적으로 운영하면서도 최적의 학습 경험을 제공할 수 있습니다.인프런의 평균 유료 강의 가격은 5만 4천 원이며, 전체 강의의 20%는 무료로 제공됩니다.일반적으로 유료 강의 비용이나 개수 한도를 설정하고, 무료 강의는 무제한으로 지원하는 방식으로 운영합니다. 02. 결제는 어떻게 진행되나요?인프런 비즈니스의 결제 방식은 교육 운영 방식에 따라 선불 충전과 후불 정산 중에서 적합한 결제 방식을 선택할 수 있습니다. ① 선불 충전(무통장 입금) 방식은 미리 예치금을 충전한 뒤, 수강 신청한 금액만큼 자동 차감되는 방식입니다. ② 후불 정산(무통장 입금 또는 카드 결제) 방식은 한 달 동안의 수강 신청 내역을 기준으로 익월 초에 결제가 진행됩니다.무통장 입금 시 세금계산서 발행이 가능합니다. 03. LMS 기능이 제공되나요?인프런 비즈니스는 기업 맞춤형 교육 운영을 지원하기 위해 관리자 페이지를 무상으로 제공합니다. 간단한 설정만으로 빠르게 교육 환경을 구축할 수 있어, 교육 담당자는 운영 부담을 줄이고, 임직원들은 자율적이고 체계적인 학습 환경에서 효과적으로 학습할 수 있습니다.관리자 페이지를 통해 강의 설정, 멤버 등록, 정산 관리 등을 간편하게 할 수 있으며, 예약 입과 및 학습 독려 메일 예약 발송 기능을 지원해 효율적인 교육 운영이 가능합니다. 또한, 실시간 학습 현황을 모니터링 할 수 있어 성과 관리가 용이하며, 임직원들의 학습 데이터를 기반으로 교육을 새롭게 기획할 수 있습니다.자체 LMS 시스템을 운영하는 기업의 경우, API 및 SSO 연동을 지원합니다. 04. 원하는 강의만 지원할 수 있나요?기업은 관리자 페이지에서 지원할 강의를 직접 설정할 수 있습니다. 특정 강의 카테고리를 지정하거나 개별 강의를 선택해 지원할 수도 있으며, 1인당 예산과 수강 신청 가능 개수를 설정해 체계적이면서도 자율적인 학습 환경을 조성할 수 있습니다.또한, 미지원 강의에 대한 설정도 가능합니다. 미지원 강의는 개인 결제로 진행하도록 설정할 수 있으며, 필요에 따라 홈페이지에서 미지원 강의를 아예 노출하지 않도록 제한할 수도 있어, 기업의 교육 운영 정책에 맞춰 유연하게 관리할 수 있습니다. 05. 강의는 어떻게 구성되어 있나요?인프런은 IT 직무에 특화된 교육 플랫폼으로, AI, 개발, 데이터 등 IT 핵심 직군을 위한 3,800개 이상의 강의를 제공합니다. 강의는 이론과 실습을 아우르는 체계적인 커리큘럼으로 구성되어 있어, 학습한 내용을 실제 업무에 적용할 수 있도록 돕습니다.또한, 인프런의 질의응답 시스템을 통해 학습자들은 강의 중 궁금한 내용을 질문할 수 있으며, 평균 48시간 내 답변을 받을 수 있습니다. 지식공유자와 AI 인턴이 학습자의 질문에 응답하며, 사후 관리까지 지원해 효과적인 학습이 이루어질 수 있도록 돕습니다.인프런 앱을 통해 출퇴근 시간에도 강의를 시청할 수 있습니다.인프런에서는 매월 평균 40~50개의 신규 강의가 개설되며, 비즈니스 멤버들에게 실시간으로 제공됩니다. 06. 수강 신청은 어떻게 이뤄지나요?수강 신청은 기업이 설정한 지원 범위(예산, 유료 강의 개수) 내에서 진행되며, 운영 방식에 따라 세 가지 방법을 선택할 수 있습니다.① 자율 수강 신청: 학습자가 직접 강의를 탐색하고 선택해 수강하는 방식으로, 개인의 관심사와 직무에 맞춘 학습 설계가 가능해 학습 효율과 만족도를 높일 수 있습니다.② 관리자 승인: 자율 수강 신청에 관리 단계를 추가한 방식으로, 관리자가 신청 내역을 승인하거나 반려할 수 있습니다.③ 관리자 수동 입과: 관리자가 특정 강의와 대상을 지정해 직접 등록하는 방식으로, 부서 별 역량 강화나 필수 교육 이수에 주로 활용됩니다.수강 신청 기간을 별도로 설정하여 운영할 수 있으며, 일반적으로 관리자 승인 단계를 두는 경우, 수강 신청 기간을 설정한 후 다음날 일괄적으로 입과하는 방식으로 운영됩니다. 07. 계정 관리는 어떻게 하나요?인프런은 1인 1계정 원칙으로 운영되며, 각 멤버는 본인 계정으로 필요한 강의를 신청하고 학습을 진행합니다.관리자는 회사 이메일을 이용해 멤버를 일괄 등록할 수 있습니다. 인프런 계정이 자동으로 생성되며, 이미 가입된 계정의 경우 비즈니스 서비스 학습 안내 메일이 발송됩니다. 아직 가입하지 않은 경우에는 인증 메일이 자동 전송되어 손쉽게 계정을 등록할 수 있습니다. 또한, 관리자 페이지에서 직접 멤버를 추가하거나 정보 변경이 가능하며, 이직 및 퇴사자의 멤버 삭제 처리도 손쉽게 진행할 수 있습니다.비즈니스 서비스 이용에 필요한 최소 인원 제한은 없습니다.인프런 비즈니스는 인원수에 따라 매달 고정 비용이 청구되는 방식이 아니므로, 등록된 멤버 중 실제로 교육에 참여한 인원의 수강 신청 내역에 대해서만 비용이 청구됩니다. 08. 수료증 발급은 가능한가요?인프런에서는 100% 완강한 경우에만 수료 처리되며, 학습자가 수료증을 직접 다운로드할 수 있도록 기능을 지원하고 있습니다. 학습자는 학습 목표 달성을 위해 수강 독려 시스템을 활용할 수 있으며, 스스로 목표를 설정하고 대시보드와 수강 독려 알림을 통해 학습을 지속할 수 있습니다. 또한, 관리자는 실시간 학습 현황을 확인하고, 자동 이메일 발송 기능을 활용해 주기적으로 학습을 독려하여 높은 참여율과 수료율을 달성할 수 있습니다.학습 현황은 관리자 페이지에서 엑셀로 다운로드할 수 있어 성과 관리에 효율적으로 활용할 수 있습니다.강의 완강 여부에 따라 수료는 시스템에서 자동 처리됩니다. 별도 수료 기준이 필요한 경우, 실시간 진도율 데이터를 활용해 수료 여부를 판별할 수 있습니다. 직원이 성장하고, 조직이 성공하는 인프런 비즈니스🔗 인프런 비즈니스 홈페이지🔗 인프런 비즈니스 콘텐츠  우리는 성장 기회의 평등을 추구합니다.©인프랩 | 경기도 성남시 분당구 판교로289번길 20, 3동 5F©InfLab. All rights reserved.

인프런비즈니스

수비드(강병수)

[인프런 비즈니스 PICK] 3월 추천 강의 안보면 후회합니다!

안녕하세요. 인프런 비즈니스팀입니다."업무 생산성? 커리어 업그레이드? 트렌드 따라잡기?"걱정 마세요! 인프런 비즈니스팀이 엄선한 강의를 소개해드리는 '인.비.픽!' 🎯업무 스킬 업그레이드는 물론, 트렌디한 최신 IT 강의까지 지금 바로 만나보세요! 🚀 📌 강의 큐레이션(1): 아바타 커뮤니티앱 만들기 (React Native) (클릭)#React Native #TypeScript #React-query #React #javaScript  ✅ 이 강의, 이런 분들께 추천해요!✔ React Native를 활용한 앱 개발에 관심 있는 개발자✔ JavaScript와 React 기초를 학습해 보신 분✔ 소셜 커뮤니티 앱 개발부터 배포까지 하나의 프로젝트를 완성해 보고 싶은 분🎯 이 강의에서 배우는 핵심 스킬🔹 React Native 기반 앱 개발iOS & Android 동시 개발컴포넌트 구조 및 UI 최적화🔹 소셜 커뮤니티 기능 개발프로필 생성, 피드 게시, 실시간 채팅 기능Firebase 기반 데이터베이스 활용🔹 풀스택 개발 경험클라이언트와 서버 연동API 설계 및 데이터 흐름 최적화🚀 이 강의를 들으면 이런 걸 할 수 있어요!✔ 백엔드와 연동하며 풀스택 개발 역량을 키울 수 있어요✔ React Native로 앱을 직접 개발하고 배포할 수 있어요✔ 단순 문법 설명이 아니라, 완성도 있는 앱을 직접 만들어볼 수 있어요🚀 React Native로 실전형 커뮤니티 앱을 만들어 보고 싶다면? 지금 바로 시작하세요! 💡🔥  📌 강의 큐레이션(2): [공식튜토리얼] Dialogue System for Unity (클릭)#Unity #Unity3d-ui  ✅ 이 강의, 이런 분들께 추천해요!✔ 게임 개발을 하면서 대화 시스템을 구현하고 싶은 개발자✔ Unity를 활용한 인터랙티브 스토리텔링에 관심 있는 분✔ NPC와 플레이어 간의 자연스러운 상호작용을 만들고 싶은 분✔ 게임 UI 및 UX 설계를 배우고 싶은 초·중급 개발자🎯 이 강의에서 배우는 핵심 스킬🔹 Unity 기반 대화 시스템 설계 및 구현게임 내 대화 흐름 관리 및 UI 디자인조건부 대화 시스템 및 플레이어 선택지 기능 구현🔹 NPC와 플레이어 상호작용 강화퀘스트 진행에 따른 대화 변화상태 기반 대화 시스템 구축🔹 Unity UI 시스템 활용대화창과 선택지 버튼 등 동적 UI 설계 및 구현텍스트 타이핑 효과와 같은 시각적 요소 추가🚀 이 강의를 들으면 이런 걸 할 수 있어요!✔ RPG, 어드벤처, 인터랙티브 스토리 게임에 활용할 수 있는 대화 시스템을 직접 제작✔ 게임 내 몰입감을 높이는 자연스러운 대화 연출 구현✔ 게임 개발자로서 Unity UI 및 데이터 관리 역량을 한 단계 업그레이드📢 더 몰입감 있는 게임을 만들고 싶다면? 지금 바로 시작해보세요! 🎮🔥  📌 스프링부트로 직접 만들면서 배우는 대규모 시스템 설계 - 게시판 (클릭)#SpringBoot #MySQL #Redis #Kafka #Java ✅ 이 강의, 이런 분들께 추천해요!✔ 스프링부트를 활용해 확장 가능한 대규모 시스템을 설계하고 싶은 개발자✔ Spring Boot, JPA, Redis, Kafka 등의 기술을 실무에서 활용하고 싶은 분✔ 백엔드 개발자로서 대용량 트래픽 처리 및 성능 최적화에 관심 있는 분🎯 이 강의에서 배우는 핵심 스킬🔹 Spring Boot 기반 대규모 시스템 설계모놀리식(Monolithic) vs 마이크로서비스 아키텍처(MSA) 이해클라우드 환경에서 확장 가능한 백엔드 설계🔹 성능 최적화 및 대용량 트래픽 대응Redis 캐싱 적용 및 DB 부하 감소 전략Kafka를 활용한 비동기 이벤트 처리🔹 실무에서 바로 사용할 수 있는 기술 스택 학습Spring Boot, JPA, MySQL, Redis, Kafka대규모 시스템을 위한 API 설계 & 성능 테스트🚀 이 강의를 들으면 이런 걸 할 수 있어요!✔ 대규모 트래픽을 감당할 수 있는 게시판 시스템을 직접 개발✔ Redis & Kafka를 활용한 성능 최적화 및 비동기 처리 적용✔ 기업 수준의 백엔드 설계를 이해하고 실무에서 활용할 수 있는 경험 확보📢 Spring Boot로 실무형 대규모 시스템을 구축하고 싶다면? 지금 바로 시작하세요! 💡🔥  직원이 성장하고, 조직이 성공하는 인프런 비즈니스🔗 인프런 비즈니스 홈페이지🔗 인프런 비즈니스 콘텐츠  우리는 성장 기회의 평등을 추구합니다.©인프랩 | 경기도 성남시 분당구 판교로289번길 20, 3동 5F©InfLab. All rights reserved.

인프런비즈니스

10

우빈님의 세심한 코드 리뷰 - 인프런 워밍업 클럽 3기 백엔드 코드 ✨

👣 발자국 책갈피인프런 워밍업 클럽 3기 백엔드 코드 발자국 1주차인프런 워밍업 클럽 3기 백엔드 코드 발자국 2주차인프런 워밍업 클럽 3기 백엔드 코드 발자국 3주차인프런 워밍업 클럽 3기 백엔드 코드 발자국 4주차✅미션PR 책갈피[미션 Day 2] 추상과 구체 예시 작성[미션 Day 4] 리팩토링 & SOLID 원칙[미션 Day 7] 리팩토링 연습[미션 Day 11] 단위테스트 작성[미션 Day 16] 레이어드 아키텍처 특징 및 테스트 작성법[미션 Day 18] Mock 어노테이션 종류 및 차이점 & BDD 패턴 매치🙊 시작이 반이다... 벌써 중간점검 ?인프런 워밍업 클럽이 시작한지 2주가 지났다.. 앞으로 남은 발자국이 2개 뿐이다.. 👣이번주에는 중간점검으로 온라인 라이브가 진행 되었다. 온라인 라이브에 대한 내용은 다음과 같다.미션 Day 4 에 대한 공통 피드백Q&A에 대한 답변미션 Day 7 코드리뷰 진행각 세션은 놀라울 정도의 세심한 우빈님의 피드백 덕분에 많은 인사이트를 얻게 되어 좋은 시간이었다. 나도 첫번째로 코드리뷰를 신청하고 라이브 마지막에 코드리뷰를 받았는데 너무 좋은 경험이었다. 💪(커피를 좋아하시기로 유명한 우빈님께 Q&A 세션 도중 저가 커피 브랜드 중 어디 브랜드가 제일 맛있냐는 질문이 나왔는데.. 드셔 보신 적이 없다고 답변해 주신 부분이 인상적이었다.. ㅋㅋ 😁)✨ 우빈님의 세심한 코드 리뷰위에서 이야기했듯이 코드리뷰를 첫번째로 신청해서 우빈님께 온라인 라이브 시간에 코드리뷰를 받았다.해당 미션은 "스터디 카페" 프로그램을 리팩토링하는 미션이였는데.. 작년 4분기에 강의를 수강했을 당시에 3번이나 진행하였다.첫 번째 리팩토링, 강의를 듣기 전에 리팩토링두 번째 리팩토링, 강의를 수강하며 리팩토링세 번째 리팩토링, 강의를 수강 후 정리하며 다시 리팩토링이번이 네 번째 리팩토링이였는데 할 때마다 왜 새로운 것인지.. 🥲그래도 손이 기억이라도 한 듯 나름 순조롭게(?) 미션을 진행하게 되었고, 추가적으로 리팩토링을 진행하였고 해당 부분을 리뷰를 받고 싶어 신청하게 되었다.(우빈님께서 4번이라는 부분에 놀라셨는지(?).. 디스코드 스레드에 댓글을 남겨주셨다! 🤣)다시 돌아와서.. 코드리뷰 받은 내용은 아래와 같다.1⃣ 중요 도메인 StudyCafePassType의 구조화 🔗 Github PR 링크StudyCafePassType 구조화 리팩토링 ♻ 리팩토링 코드public enum StudyCafePassType implements PassTypeSelectable, PassTypeFormatter { // 📝 인터페이스 구조화 HOURLY("시간 단위 이용권") { // 📝 사용자 입력에 대한 구조화 @Override public boolean selected(String userInput) { return "1".equals(userInput); } // 📝 사용자 출력 포맷에 대한 구조화 @Override public String format(StudyCafePass pass) { return String.format("%s시간권 - %d원", pass.getDuration(), pass.getPrice()); } } } ✏ 우빈님 리뷰Q. 클래스 내부에서 사용자 입력값 및 출력값에 사용하는 인터페이스를 구현함으로써 오버 엔지니어링이 된 것 같은 느낌이 드네요.. 🤦‍♂️ A. 저도 그렇게 생각해요.. ㅋㅋㅋㅋ 오버 엔지니어링이기보다 PassType은 중요한 도메인 모델인데, Input에서만 의미를 가지는 사용자 선택지가 침투하고 있다. 사용자 선택 방법이 "a", "b", "c"로 바뀐다면? 단순히 입력 방식을 바꿨을 뿐인데 무료 도메인 모델이 수정되어야 하는 엄청난 사태가 발생한다. 항상 구조화를 하는 것이 정답은 아니다. Output format도 마찬가지이다. 책임이 우선이다. 적절한 책임의 분배가 객체의 결합도를 낮추고 응집도를 높이는 것이다.  🤔 돌아보기단순히, OCP를 적용하기 위해 접근해서 리팩토링 했었는데..적절한 객체 책임 분리를 하지 못했으며, 중요한 도메인 모델을 수정하는 엄청 큰 사이드 이펙트가 일어날 수 있다는 점을 간과 했다는 것이다.다음부터는 구조화를 남발하지 않고 책임에 집중해서 리팩토링 해야겠다..2⃣ 이용권을 읽는 부분과 읽은 부분의 개념을 추출하여 객체 분리 🔗 Github PR 링크ReadLockerPasses 객체 분리 ♻ 리팩토링 코드public class ReadLockerPasses { // 📝 LockerPasses를 해석하는 객체 분리 private final List<StudyCafeLockerPass> passes; private ReadLockerPasses(List<StudyCafeLockerPass> passes) { this.passes = passes; } // 📝 lines을 해석하여 List<StudyCafeLockerPass> 객체를 만들어준다. public static ReadLockerPasses ofLines(List<String> lines) { List<StudyCafeLockerPass> passes = lines.stream() .map(ReadLockerPasses::ofLine) .toList(); return new ReadLockerPasses(passes); } private static StudyCafeLockerPass ofLine(String line) { String[] values = line.split(CSV_SPLITTER); // ⭐️ CSV라는 방식에 종속적 StudyCafePassType studyCafePassType = StudyCafePassType.valueOf(values[0]); int duration = Integer.parseInt(values[1]); int price = Integer.parseInt(values[2]); return StudyCafeLockerPass.of(studyCafePassType, duration, price); } // 📝 StudyCafeLockerPasses를 생성해준다. public StudyCafeLockerPasses toPasses() { return StudyCafeLockerPasses.of(passes); } } ✏ 우빈님 리뷰Q. 일급 컬렉션을 적용하기 위해 toPasses() 메서드를 생성했는데 Read~라는 네이밍을 가진 클래스에 많은 책임이 부여된 것 같아 네이밍이 모호한 것 같습니다. 좋은 방법이 있을까요..? 🧐 A. 많은 책임이라고 생각하신 이유가 있을까요? "ReadLockerPasses는 어디선가 읽은 lines를 가지고 StudyCafeLockerPasses를 만들어준다"의 책임으로 보여서, 어색하지 않으며 테스트 코드 작성도 가능하다. 그와 별개로 CSV라는 방식에 종속되어있다. CSV형식이 다른 방식으로 바뀌었을 때 같이 바뀌어야 하는 부분이 CSV_SPLITTER 부분이다. 의도한 것 이라면 상관없다.  🤔 돌아보기Read라는 클래스명을 가지고 있어 StudyCafeLockerPasses를 생성해주는 메서드가 존재해 많은 책임이 있다고 생각했는데..우빈님 리뷰 이후에 다시 보니.. 그렇게 어색한가 싶기도 하다.. ㅎㅎ해당 클래스의 작성 당시 CSV 방식을 의존하려는 의도는 없었다. 단순히 읽은 부분의 개념을 추출한 것인데.. 위의 클래스는 CSV_SPLITTER상수가 사용되어 의도하지 않게 CSV라는 방식에 종속적이게 된 것이다.CSV라는 방식이 변경되면 객체 로직이 바뀌어야 한다.객체 구현 시, 종속성에 대해서 방어적으로 접근할 필요가 있어보인다.3⃣ ProvideException 커스텀 예외🔗 Github PR 링크ProvideException 커스텀 예외 ♻ 리팩토링 코드// 📝 이용권을 가져오는 과정에서 생긴 에러의 커스텀 예외 클래스 생성 public class ProvideException extends RuntimeException { public ProvideException(String message) { super(message); } } public class StudyCafePassMachine { public void run() { try { outputHandler.showPassOrderSummary(order); } catch (AppException e) { outputHandler.showSimpleMessage(e.getMessage()); } catch (ProvideException e) { // 📝 커스텀 예외 catch outputHandler.showSimpleMessage("이용권을 제공받을 수 없습니다."); } catch (Exception e) { outputHandler.showSimpleMessage("알 수 없는 오류가 발생했습니다."); } } } ✏ 우빈님 리뷰Q. AppException 성격이랑 다른 것 같다고 생각되어 Provider 인터페이스에서 발생하는 예외 클래스 ProvideException를 별도로 생성하였습니다. A. 혹시 어떻게 다르다고 생각셨나요?? AppException의 의도는, 프로그램에서 발생할 수 있는 대부분의 애플리케이션 상황을 정의하는 최상위 예외 클래스이다. 만약 ProvideException을 별도로 표기하여 더 구체적인 상황을 나타내고 싶으면, AppException을 상속받아서 구성해야 한다. 그렇지 않으면 커스텀 예외 클래스가 늘어남에 따라 catch절도 같이 늘어날 것이다. 추가적으로, "이용권을 제공받을 수 없습니다."라는 메시지가 사용자 친화적이지 않다.  🤔 돌아보기리팩토링 당시, 초기 이용권을 가져와야만 프로그램이 실행된다는 관점에서 ProvideException의 커스텀 예외 클래스를 작성하였다.하지만 이용권을 가져오는 부분은 프로그램 내부에서 필요한 시점마다 호출하고 있어우빈님 리뷰대로 AppException 클래스를 상속받아서 작성하는 것이 더 나은 설계 같다.예외 메세지도 사용자 관점에서는 친화적이지 않은 것이 분명하다.내가 키오스크 시스템을 사용하다가 저런 메세지를 마주한다면... 화가 날 것 이다... 😡프로그램의 의도를 정확히 파악할 필요가 있어보인다. 또한 예외 메세지도 누가 보는지에 따라 고민해보는 습관을 길러야겠다.이렇게, 요청한 3개의 리뷰와 2개의 추가 리뷰를 받아 보았다..고작 3일 만에 7명이나 리뷰를 해주셨는데 세심하고 또 세심했다... 퀄리티가 상당했다.. ✨이번 온라인 라이브를 통해 우빈님에 대한 팬심과 존경심이 더욱 커졌다....! 📈리뷰해주신 내용으로 다시 리팩토링을 함으로써 한층 더 Readable Code에 대한 성장을 경험할 수 있었다. 🚀💡 자기만의 언어로 강의 키워드 정리하기 섹션 6. 코드 다듬기좋은 주석 - 주석의 양면성주석이 많다는 것 : 추상화가 덜 되고 가독성이 좋지 않은 코드 (코드 품질 저하 📉)주석이 필요한 경우 : 히스토리를 알 수 없을 경우, 주석으로 상세히 설명변수와 메서드 나열 순서변수 : 사용하는 순서대로 위치한다. (인지적 경제성 / 뇌 메모리 줄이기)객체의 공개/비공개 메서드 : 공개 메서드를 상단에 위치하고, 비공개 메서드 하단에 위치한다. 공개 메서드 중에서도 중요도의 순서에 따라 배치한다.공개 메서드 : 객체의 상태를 변경 하는 부분이 가장 상단에 위치하도록 - 상태 변경 >>> 판별 >= 조회비공개 메서드 : 출현한 순서대로패키지 나누기여러 파일들의 네임 스페이스를 관리하기 때문에 적당한 수준으로 잘 나누어야 한다.대규모 패키지 변경은 팀원과의 합의 필요 -> 추후 conflict가 생길 수 있다.기능 유지보수하기정렬 단축키, linting, style - sonarlint, editorconfig섹션 7. 리팩토링 연습메서드 추출로 추상화 레벨 맞추기Optionalreturn null / Optional 파라미터 사용은 안티패턴이다.객체에 메시지 보내기객체를 존중하고 메시지를 보내자.객체의 책임과 응집도⭐️ 추상화 관점의 차이 - FileHandler구현에 초점을 맞춘 추상화 VS 도메인 개념에 초점을 맞춘 추상화File을 read하는 부분의 로직들은 전부 FileHandler에 들어갈 것이다. 잘못된 객체 응집일 수도 있다..방법에 초점을 맞춘 설계 방식이 아닌 어떤 데이터를 가져오는 가에 대한 초점을 맞추는 것이 좋다.섹션 8. 기억하면 좋은 조언들능동적 읽기가지고 있는 리팩토링 기법들을 총동원해서 읽자. -> 리팩토링하면서 읽기눈으로만 보는 수동적 읽기는 권장하지 않는다.도메인 지식을 늘리기 위해서 능동적 읽기가 필요하다. (작성자의 의도 파악)오버 엔지니어링필요한 적정 수준보다 더 높은 수준의 엔지니어링예시 1. 구현체가 하나인 인터페이스구현체가 수정할 때마다 인터페이스도 수정해야 함코드 탐색의 어려움예시 2. 너무 이른 추상화정보가 숨겨지기 때문에 복잡도가 높아진다.후대 개발자들이 선대의 의도를 파악하기가 어렵다.은탄환은 없다클린 코드도 은탄환이 아니다.실무에서의 줄다리기지속 가능한 소프트웨어 품질 VS 기술 부채를 안고 가는 빠른 결과물 -> 클린 코드를 대비한 코드 센스가 필요하다.모든 기술과 방법론은 적정 기술의 범위 내에서 사용되어야 한다.항상 정답인 기술은 없다.한계까지 연습해보고, 적정 수준, 적정 시점을 깨닫는 것이 필요하다.섹션 3. 단위 테스트단위 테스트작은 코드(클래스 또는 메서드) 단위를 독립적으로 검증하는 테스트 -> 가장 기본이 되는 테스트검증 속도가 빠르고, 안정적수동 테스트, 자동화 테스트 -> 인지 필요사람이 검증하는 수동 테스트 -> sout으로 출력하고 눈으로 직접 확인기계가 검증하는 자동화 테스트Junit5, AssertJJunit5 : 단위 테스트를 위한 테스트 프레임워크AssertJ : 테스트 코드 작성을 원할하게 돕는 테스트 라이브러리 - 풍부한 API 메서드 체이닝 지원해피 케이스, 예외 케이스 -> 테스트 케이스 세분화예외 케이스 : 암묵적 혹은 드러나지 않은 요구사항에서 발견경계값 테스트범위, 구간, 날짜 경계값들로 테스트를 해야한다.테스트하기 쉬운/어려운 영역 (순수함수)테스트 하기 어려운 영역을 구분하고 분리하기외부로 분리할수록 테스트 가능한 코드는 많아진다.테스트하기 어려운 영역관측할 때마다 다른 값에 의존하는 코드 : 현재 날짜/시간, 랜덤 값, 전역 변수/함수, 사용자 입력외부 셰계에 영향을 주는 코드 : 표준 출력, 메시지 발송, 데이터베이스에 기록하기순수 함수 - 테스트하기 쉬운 영역같은 입력에는 항상 같은 결과외부 세상과 단절된 형태lombok@Data, @Setter, @AllArgsConstructor 지양양방향 연관관계 시 @ToString 순환 참조 문제섹션 4. TDD: Test Driven DevelopmentTDD 테스트 주도 개발 (Test Driven Development)로, 프로덕션 코드보다 테스트 코드를 먼저 작성하여 테스트가 구현 과정을 주도하도록 하는 방법론선 기능 구현, 테스트 작성의 문제점 (일반적인 개발) - 구현순서 : 기능 -> 테스트테스트 자체의 누락 가능성해피 케이스만 검증할 가능성잘못된 구현을 다소 늦게 발견할 가능성선 테스트 작성, 기능 구현 (TDD) - 구현순서 : 테스트 -> 기능복잡도(유연하며 유지보수가 쉬운)가 낮은 테스트 가능한 코드로 구현할 수 있게 한다.테스트가 힘든 코드를 위한 코드 작성이 가능예를 들면 LocalDateTime.now()의 경우 외부세계로 분리해서 테스트를 하기 편한 코드를 작성할 수 있다.프로덕션 코드를 작성한 후 테스트 코드를 작성하기 귀찮을수도..쉽게 발견하기 어려운 엣지 케이스를 놓치지 않게 해준다.구현에 대한 빠른 피드백을 받을 수 있다.과감한 리팩토링이 가능해진다.테스트 코드가 보장TDD의 관점이전의 관점 : 테스트는 구현부 검증을 위한 보조 수단변화된 관점 : 테스트와 상호 작용하며 발전하는 구현부레드 - 그린 - 리팩토링Red : 실패하는 테스트 작성Green : 테스트 통과 하는 최소한의 코딩Refactor : 구현 코드 개선 테스트 통과 유지애자일 방법론 vs 폭포수 방법론애자일 방법론 https://agilemanifesto.org/iso/ko/manifesto.html반복적 개발(Iterative Development): 짧은 개발 주기(스프린트)를 반복하며 지속적으로 개선.유연성: 요구사항 변경을 수용할 수 있도록 유동적으로 진행.고객 참여: 개발 과정에서 지속적인 피드백을 반영하여 사용자 중심 개발 가능.자율적인 팀 구성: 개발팀이 자체적으로 의사 결정을 하며, 빠르게 문제를 해결함.폭포수 방법론단계적 개발: 요구 분석 → 설계 → 구현 → 테스트 → 배포 → 유지보수 순서로 진행됨.문서 중심: 각 단계마다 문서화가 철저하게 이루어짐.선형 구조: 이전 단계가 완료되어야 다음 단계로 넘어갈 수 있음.변경이 어려움: 초기에 요구사항을 확정하면 이후 변경이 어렵고 비용이 많이 듦.익스트림 프로그래밍XP(Extreme Programming, 익스트림 프로그래밍)는 애자일 방법론 중 하나로, 빠른 개발 주기와 지속적인 피드백을 중심으로 하는 소프트웨어 개발 방법론이다. 고객의 요구사항 변화에 빠르게 대응할 수 있도록 짧은 개발 반복 주기(Iteration)와 강한 협업 문화를 강조한다.스크럼, 칸반스크럼애자일 프레임워크로, 일정한 스프린트 동안 작업을 계획하고 진행하는 반복적이고 점진적인 개발 방식이다. 짧은 개발 스프린트를 통해 빠르게 결과물을 만들고 지속적으로 개선하는 것이 핵심이다.1⃣ 백로그 작성 – 제품 백로그에 모든 요구사항을 정리2⃣ 스프린트 계획 – 스프린트 기간 동안 수행할 작업 선정3⃣ 스프린트 진행 – 개발 진행 및 매일 스탠드업 미팅4⃣ 스프린트 리뷰 – 개발 완료된 기능을 검토5⃣ 회고(Retrospective) – 개선점을 찾고 다음 스프린트에 반영칸반Workflow와 가시성을 중심으로 한 애자일 프레임워크로, 지속적인 개선과 작업량 관리를 중점적으로 다룬다. 작업을 시각적으로 표현하여 현재 진행 상황을 쉽게 파악할 수 있도록 합니다.1⃣ Backlog: 해야 할 작업을 모아둠2⃣ To Do: 현재 진행할 작업3⃣ In Progress: 진행 중인 작업4⃣ Review/Test: 리뷰나 테스트가 필요한 작업5⃣ Done: 완료된 작업섹션 5. 테스트는 []다.테스트 코드는 문서다.프로덕션 기능을 설명해주는 것이 테스트 코드 문서다.다양한 테스트 케이스를 통해 프로덕션 코드를 이해하는 시각과 관점을 보완할 수 있다.고민했던 내용(테스트 코드)을 팀 자산(소스 코드)으로 공유할 수 있다.@DisplayName - 도메인 정책, 용어를 사용한 명확한 문장메서드명만으로 어떤 것을 검증하고자 하는 의도 파악이 어려움Junit5에 추가한 어노테이션이다.문장 형태로 섬세하게 테스트 검증에 대한 내용을 어노테이션안에 작성한다.섬세한 DisplayName특정 시간 이전에 주문을 생성하면 실패한다. ❌영업 시작 시간 이전에는 주문을 생성할 수 없다. ✅도메인 용어를 사용하여 추상화된 내용을 담기 -> 메서드 자체의 관점 보다 도메인 정책 관점 (특정 시간 -> 영업 시작 시간 ✅)테스트의 현상을 중점으로 기술하지 말 것 (~실패한다 ❌)Given / When / Then - 주어진 환경, 행동, 상태 변화Given : 시나리오 진행에 필요한 모든 준비 과정When : 시나리오 행동 진행Then : 시나리오 진행에 대한 결과 명시, 검증TDD vs BDDBDDTDD에서 파생된 개발 방법시나리오 기반한 테스트 케이스 자체에 집중하여 테스트한다.Junit vs SpockSpock은 Groovy언어로 BDD 패턴을 적용해서 테스트 코드를 작성할 수 있다.언어가 사고를 제한한다.명확하지 못한 테스트 코드는 사고를 제한할 수 있다.문서로서의 테스트를 신경 쓸 필요가 있다. 🏃 돌아보며..미션과 발자국을 정신없이 진행하다 보니 벌써 남은 인프런 워밍업 클럽도 2주밖에 남지 않았다. 처음에 OT 라이브 당시 러너가 120명 정도였는데, 이번 중간 점검 라이브 때는 60명 정도로 줄어들었다.강의 내용 자체는 어렵지 않지만.. 2개의 강의(14시간 + 12시간)를 한 달만에 들으면서 미션과 발자국을 진행하는 건 쉽지 않아 보인다.. 나는 미리 강의를 수강해서 다행이다라는 생각이 든다.. 😅하지만, 쉽지 않은 만큼 성실히 참여한다면 단기간 내 성장하는 데 큰 도움이 될 것이다.그리고 이번 주에 코드리뷰를 신청하기 잘했다는 생각이 들었다. 🍀 위에서도 여러 번 언급했지만 우빈님의 세심한 리뷰 탓(?)에내가 미션을 수행하는 데 있어 우빈님보다 세심하게 집착 했었나..? 반성하게 된다... 😭 마지막 주차 온라인 라이브에서도 테스트 코드에 대한 코드리뷰가 진행된다고 한다.기회가 된다면 또 한 번 코드리뷰를 받아 단골 손님이 되고 싶다. 😂2주를 걸쳐, 읽기 좋은 코드의 스터디 과정은 이번주로 막을 내렸다.다음 주차부터는 테스트 코드에 대해 본격적으로 스터디하는 과정이 진행된다.남은 2주도 화이팅하며 좋은 성장을 이루길 기대해 본다. 🔥 끝으로, 3월 중순이 되니 이제 슬슬 봄 내음이 나는 것 같다.. 🌸얼어붙은 개발 시장에도 봄이 찾아왔으면 좋겠다.. 🧊 발자국 2주차 끄읕 ! [출처]인프런 워밍업 클럽 : https://www.inflearn.com/course/offline/warmup-club-3-be-code강의 : https://www.inflearn.com/course/readable-code-%EC%9D%BD%EA%B8%B0%EC%A2%8B%EC%9D%80%EC%BD%94%EB%93%9C-%EC%9E%91%EC%84%B1%EC%82%AC%EA%B3%A0%EB%B2%95/dashboard

백엔드인프런워밍업클럽백엔드3기발자국박우빈클린코드읽기좋은코드

10 2일 전
leeebug

워밍업 클럽 스터디 3기 FS - 2주차 발자국

인프런 워밍업 클럽 스터디에 참여하고 벌써 2주 차도 마무리에 접어들고 있다. 4주간의 스터디이기 때문에 생각보다 일정이 타이트하여 시간관리가 무엇보다도 중요한 시기라고 생각한다.이번 주에는 파일 업로드 기능을 구현해야하기에 1주 차 과제를 급하게 마무리하고 지난주 토요일부터 서둘어서 미리 학습을 시작했다.깃 레포지토리의 경우에는 지난주에 사용했던 템플릿을 거의 수정없이 그대로 사용해서 개발환경 구축은 크게 어렵지 않았다.이번 주 강의에서는 Supabase Storage를 사용했는데 API가 잘 준비되어있어서 사용 방법을 익히는데도 크게 어렵지는 않았다. 다만 아래에서도 언급하겠지만 Supabase Storage는 AWS S3 기반으로 구현되어 강력한 네이밍 규칙이 적용되어 개인적으로는 Supabase Storage와 Supabase DB를 함께 사용했다. 📝 2주차 학습Supabase Storage클라우드 기반 객체 저장소로, AWS S3와 유사한 방식으로 파일을 저장하고 관리하는 서비스파일 및 이미지 업로드 및 관리 기능 제공PostgreSQL과 연동 가능권한 관리(RLS) 및 퍼블릭/프라이빗 파일 설정 가능Supabase SDK 또는 Restful API로 사용 가능  ✔ 파일명 규칙 (Supabase & AWS 공통) ASCII 문자, 숫자, 일부 특수 문자 허용 (- _ . /) 파일명을 /로 구분하여 폴더처럼 사용 가능 (folder/image.png) 공백 포함 가능하지만, URL Encoding이 필요할 수 있음 파일명에 한글, 이모지, 특수문자가 포함될 경우 정상적으로 업로드되지 않을 가능성이 있음 → URL-safe 변환 권장 React DropzoneReact에서 간편하게 파일 Drag & Drop 기능을 구현할 수 있는 라이브러리HTML5 File API를 활용하여 파일 업로드를 쉽게 구현할 수 있는 기능을 제공파일 타입, 크기, 개수 등 다양한 제약 조건 설정 가능비동기로 파일을 처리할 수 있는 onDrop 이벤트 제공 📋 2주차 미션💬 GitHub 저장소🚀 데모 영상 보러가기미션 해결 과정 요약2주 차 미션은 Next.js, React Query, TailwindCSS를 사용하여 이미지 업로드 앱을 구현하기였다. 필수 구현 기능으로는 이미지 업로드 기능(클릭 업로드 방식과 Drag & Drop 방식, 다중 업로드)과 이미지 삭제와 이미지 검색 기능 구현하기였다. 추가 기능은 파일의 마지막 수정 시간을 화면에 출력하는 UI 구현하기였다. 여기에 과제의 완성도를 높이기 위해서 개인적인 챌린지로 파일명에 한글 또는 특수문자 포함된 파일 업로드 기능, 1MB 미만으로 이미지를 압축하는 기능, 다운로드 기능을 추가로 구현하였다. 과제 추가 구현 기능✅ 마지막 수정 시간 표시const { error: insertError } = await supabase.from(DB_TABLE_NAME).insert({ name: file.name, originalName: originalFileName, imageId: uploadedFile.id, imageUrl: publicUrl, createdAt: new Date(file.lastModified).toISOString(), })생성: DB에 파일 데이터 업로드 시 createdAt에 file.lastModified를 ISOString 형식으로 저장 if (dbData) { const { error: updateError } = await supabase .from(DB_TABLE_NAME) .update({ name: file.name, originalName: originalFileName, imageUrl: publicUrl, updatedAt: new Date().toISOString(), }) .eq('imageId', uploadedFile.id)수정: DB에 해당 ID가 존재할 경우 updatedAt(string | null)에 현재 시간을 ISOString 형식으로 저장// DropImageManager 컴포넌트에서 생성 시간, 수정 시간을 포멧팅하여 DropImage 컴포넌트에 프롭스로 전달 const localCreatedAt = getLocalTime(image.createdAt) const localUpdatedAt = image.updatedAt ? getLocalTime(image.updatedAt) : null <!-- JSX 정렬이 잘 안되서 렌더링 형태만 봐주세요! --> <div className="w-5/6 truncate"> <span className={`text-[0.7rem] font-semibold ${localUpdatedAt ? 'text-mint-800' : 'text-gray-500'}`} > {localUpdatedAt ? localUpdatedAt : localCreatedAt} </span> {localUpdatedAt && ( <span className="text-[0.7rem] font-semibold text-mint-800"> (수정)</span> )} </div>출력: updatedAt이 존재할 경우 updatedAt과 (수정) 을 함께 출력, updatedAt: null이라면 createdAt를 출력 개인 챌린지 기능✅ 파일명 자동 변환 후 이미지 업로드하는 기능을 구현 (UX 개선)파일명 검증: 정규식을 활용하여 한글 및 특수 문자 포함 여부를 확인자동 변환: 검증 후 8자리 랜덤 문자열로 안전한 파일명 생성업로드 처리: 변환된 파일명으로 File 객체 생성 후 formData.append로 원본 파일명 함께 전송서버 액션: Supabase Storage에 저장 후, 완료 시 DB에 메타데이터 저장하여 연동결론: 파일명 변환을 자동화하여 업로드 오류를 방지하고, 원본 파일명도 유지하여 검색 및 관리 UX 개선✅ 파일 용량이 1MB 초과 시 자동 압축 후 업로드하는 기능을 구현 (UX 개선)browser-image-compression 라이브러리를 사용하여 파일의 용량 검증 후 1MB 초과 시 이미지 압축 후 업로드결론: 이미지 최적화로 업로드 속도 향상, 스토리지 비용 절감 효과✅ Blob URL을 활용한 다운로드 기능 추가 (UX 개선)Blob URL 생성: 업로드된 이미지를 fetch()로 가져와 Blob으로 변환다운로드 기능 구현: window.URL.createObjectURL(blob)으로 브라우저에서 직접 다운로드 가능하도록 처리결론: Blob URL 다운로드 방식을 적용하여 최적화된 이미지를 빠르게 다운로드 받을 수 있도록 개선🚧 기능 구현 시 어려웠던 부분Supabase Storage에 전달하는 File 객체 커스텀 불가원본 파일명을 추가하려 했으나, File 객체 자체를 수정하는 것이 제한적이다.파일 객체를 복사하여 원본 파일명을 추가하는 방법 시도전개 연산자를 사용하여 객체 복사 후 원본 파일명을 추가하려 시도하였으나 file 객체는 일반적인 방법으로는 복사할 수 없는 특별한 객체이다.ExtendedFile 확장 클래스로 인스턴스를 생성했으나 서버에 전달되지 않는 문제 발생확장된 ExtendedFile 객체를 formData에 담아 서버로 전송했지만, 서버에 정상적으로 전달되지 않았다.최종 해결 방법formData.append("file", file) formData.append("originalFileName, file.name)file 객체와 원본 파일명을 함께 서버로 전송 후 가공하여 Supabase Storage의 파일명에는 안전한 파일명만 저장하고 DB에 스토리지Id, 원본 파일명, 안전한 파일명, 이미지URL 등 정보를 저장했다. 🧾 ERD 다이어그램👀 2주차 회고아직 갈 길이 멀지만, 리팩토링을 통해 Next.js의 장점을 살릴 수 있는 구조로 점점 개선되어가는 과정을 경험하면서 이번 주 역시 알차게 보냈다고 생각한다.이번 주는 특히 MVP 패턴과 비슷한 형태로 컴포넌트 구조를 잡는 것에 익숙해지는 것을 개인적인 목표로 삼았다. 처음부터 MVP 패턴을 염두해 두고 설계한 것은 아니었지만, 진행하다보니 자연스럽게 MVP와 유사한 패턴으로 정리되어 가는 것을 느꼈다.화면 렌더링 시 상호작용이 필요하지 않은 정적인 요소들까지 클라이언트 컴포넌트로 관리하면 불필요한 하이드레이션 부담이 증가할 수 있다는 점을 다시 한번 체감했다.클라이언트 컴포넌트 내에서도 역할을 나눠 서비스 레이어나 상태 관리만 담당하는 매니져 컴포넌트와 프롭스로 상태를 전달받아 단순히 화면을 렌더링을 담당하는 UI 컴포넌트로 분리하는 연습을 진행했다.이러한 구조로 개선하면서 클라이언트 컴포넌트의 부담을 줄이고, 유지보수성을 높이는 방향으로 점차 최적화되고 있다는 점이 느껴졌다. 아직 개선해야 할 부분이 많지만 점진적으로 개선하여 더 나은 아키텍쳐를 만들어가는 과정이 의미 있었다고 생각한다.

풀스택워밍업클럽3기발자국회고과제미션

제이든(양준식)

인프런 비즈니스 시작하기 STEP 5

인프런 비즈니스 고객사는 기업 맞춤형 교육을 지원하기 위해 관리자 페이지를 무상으로 제공합니다. 매뉴얼 가이드를 따라 기업에 최적화된 교육 운영 환경을 쉽고 빠르게 구축해보세요. 교육 담당자는 운영 부담을 줄이고, 학습자는 체계적인 환경에서 자율적으로 학습할 수 있습니다. 인프런 비즈니스 시작하기 STEP 501. 인프런 비즈니스 서비스 가입하기02. 결제 수단 등록하기03. 지원 강의 설정하기04. 멤버 등록하기05. 기타 운영 설정 요청하기 01. 인프런 비즈니스 서비스 가입하기인프런 비즈니스 서비스는 별도 비용 없이 바로 가입할 수 있습니다. 인프런 계정만 있으면 가입과 동시에 새로운 그룹이 생성되고, 관리자 페이지가 제공됩니다.[1단계] 인프런 회원 가입(링크) 인프런 회원가입 : https://www.inflearn.com/user/signup비즈니스 서비스 가입을 위해 인프런 계정이 필요합니다.해당 계정은 기본 관리자로 등록됩니다. (추후 수정 및 추가 가능)[2단계] 인프런 비즈니스 서비스 가입(링크) 인프런 비즈니스 : https://www.inflearn.com/intro-group로그인 후, 인프런 비즈니스 홈에서 [시작하기] 버튼을 눌러 가입을 시작합니다.이용 약관에 동의하고, 회사 및 담당자 정보를 입력한 뒤 제출합니다.[3단계] 관리자 페이지 확인가입 완료 후, 인프런 홈페이지 우측 상단에 [비즈니스] 버튼이 나타납니다.정상적으로 관리자 페이지에 접근할 수 있는지 확인해주세요. 02. 결제 수단 등록하기결제 수단을 등록하면 관리자 페이지의 기능이 활성화됩니다. 인프런 비즈니스 서비스는 후불정산과 선불충전 중 선택할 수 있으며, 기업에 적합한 방식으로 등록해주세요.[1단계] 결제 수단 등록 안내 클릭관리자 페이지 상단에 "결제 수단을 등록해주세요"라는 노란색 배너 알림이 표시됩니다.우측의 [등록하기] 버튼을 클릭합니다.[2단계] 결제 방식 및 수단 선택후불정산 : 카드결제 또는 무통장입금(세금계산서 발행) 방식으로 결제할 수 있습니다. 선불충전 : 무통장입금 방식으로 예치금을 미리 충전한 후, 수강 금액에서 차감됩니다.[3단계] 카드정보 / 세금계산서 수신 이메일 입력카드 결제 시 : 카드 정보를 입력해주세요.무통장 입금 시 : 세금계산서 수신 이메일을 입력해주세요.(** 참고) 후불정산은 월 단위로 진행됩니다.수강 내역 발송 : 익월 2영업일 내후불 결제 진행 : 수강 내역 확인 후, 이상 없을 시 6영업일 이내 / 매월 10일 이전(** 참고) 선불충전 방식 이용 시 사전 문의가 필요합니다.결제 수단 등록 → 예치금 충전 문의 → 세금계산서 발행 및 입금확인 → 예치금 충전 완료 → 자동 정산문의: group@inflab.com 03. 지원 강의 설정하기교육에 필요한 강의를 설정하세요. 지원 강의 선택은 총 3단계로 진행되며, 임직원의 IT 역량 강화 목표에 맞춰 구성할 수 있습니다. 지원 범위 내에서 임직원들은 자유롭게 수강 신청하고 학습할 수 있습니다.[1단계] 가격 및 카테고리 설정강의 1개당 최대 가격을 지정할 수 있으며, 초과 시 미지원 강의로 자동 분류됩니다.카테고리별 지원 여부를 설정할 수 있으며, 하위 카테고리도 선택적으로 지정할 수 있습니다.[2단계] 필수/제외 강의 설정반드시 지원해야 하는 강의 또는 제외할 강의를 선택합니다.1단계 카테고리 설정과 별도로 지원/미지원 강의를 추가로 지정할 수 있습니다.[3단계] 최종 지원 강의 확인1단계와 2단계에서 설정한 내용을 반영한 최종 강의 목록을 확인합니다.[최종 선택 강의 목록 다운로드] 기능을 활용해 쉽게 확인할 수 있습니다.(** 참고) 미지원 강의 설정미지원 강의는 개인 결제로 수강할 수 있도록 설정할 수 있습니다.또는, 홈페이지에서 아예 노출되지 않도록 설정할 수도 있습니다.  04. 멤버 등록하기인프런 비즈니스 서비스에 구성원을 멤버로 등록해주세요. 기업 이메일을 활용하면 멤버 등록과 관리가 더욱 간편하며, 등록 즉시 자동 이메일 안내가 발송되어 학습을 빠르게 시작할 수 있습니다.1. 멤버 일괄 등록하기좌측 상단의 [리스트 다운] 버튼을 클릭하여 .csv 파일을 다운로드합니다.새롭게 등록할 멤버 정보를 파일에 입력합니다.원활한 관리를 위해 정확한 정보 입력을 권장드립니다. (** 필수 항목: 사번/학번, 이메일, 이름)[리스트 업로드] 버튼을 클릭하여 멤버 일괄 등록을 완료해주세요.(** 주의) 리스트 업로드 시 기존 명단이 교체됩니다. 기존 멤버에 새로운 멤버를 추가하려면, 기존 명단 아래 새로운 멤버 정보를 추가 입력한 후 업로드하세요.2. 멤버 개별 등록하기우측 상단의 [멤버 추가] 버튼을 클릭하여 개별 멤버를 등록할 수 있습니다.원활한 관리를 위해 정확한 정보 입력을 권장드립니다. (** 필수 항목: 사번/학번, 이메일, 이름) (** 참고) 멤버 등록 시 멤버 상태는 인프런 회원가입 여부에 따라 인증/미인증으로 구분됩니다.인증: 회원가입이 완료된 상태로 수강 신청 가능함. 멤버 등록 즉시 가입 완료 메일 발송되며, 학습 시작 안내 포함됨. 미인증: 회원가입이 완료되지 않은 상태로 비밀번호 설정 후 수강 신청 가능함. 등록 시 비밀번호 설정 안내 메일 발송되며, 설정 완료 시 상태가 '인증'으로 변경됨.  05. 기타 운영 설정 요청하기인프런 비즈니스 관리자에서 제공되는 기능 외에도, 인프런 어드민을 통해 기업 맞춤형 교육 환경을 구축할 수 있습니다. 필요한 설정이 있다면, 언제든 인프런 매니저에게 요청해주세요.1. 서비스 이용 기간수강 신청이 가능한 서비스 이용 기간을 설정할 수 있습니다.예를 들어, 연 단위로 교육을 운영하는 경우 1월 1일부터 12월 31일까지로 설정하고, 인당 수강 한도를 함께 지정하면 해당 기간 동안 정해진 범위 내에서 학습을 진행할 수 있습니다.2. 수강신청 기간수강 신청 기간을 매월 특정 기간(n일부터 n일까지)으로 설정할 수 있습니다.보통 수강 신청 기간을 운영할 경우, 관리자 승인 단계를 추가하고, 정해진 날짜에 교육을 시작합니다.(예시) 매월 1일 ~ 10일 수강 신청 > 11일 관리자 일괄 승인/반려 처리 > 15일 교육 시작3. 수강 승인 방식수강 승인 방식은 자동 승인과 수동 승인 중 선택할 수 있습니다.자동 승인: 학습자가 수강 신청하면 즉시 강의를 시청할 수 있습니다.수동 승인: 관리자가 수강 신청 내역을 검토한 후 승인 또는 반려할 수 있습니다.4. 1인당 수강 한도 설정 (금액, 개수)학습자가 신청할 수 있는 최대 금액과 유료 강의 개수를 설정할 수 있습니다.기업은 예산 범위 내에서 교육을 운영할 수 있으며, 학습자는 지원 한도 내에서 자유롭게 학습을 진행할 수 있습니다.일반적으로 서비스 이용 기간을 설정할 경우, 수강 한도도 함께 설정합니다5. 미지원 강의 구매 및 노출 여부기업이 지원하지 않는 강의에 대해 개인 구매 허용 여부 및 홈페이지 노출 여부를 설정할 수 있습니다.기본 설정에서는 미지원 강의도 홈페이지에 함께 노출되며, 학습자가 신청 시 개인 결제로 진행됩니다. 직원이 성장하고, 조직이 성공하는 인프런 비즈니스🔗 인프런 비즈니스 홈페이지🔗 인프런 비즈니스 콘텐츠  우리는 성장 기회의 평등을 추구합니다.©인프랩 | 경기도 성남시 분당구 판교로289번길 20, 3동 5F©InfLab. All rights reserved.

인프런비즈니스

lkwo

워밍업 클럼 3기 BE 클린코드 1주차 발자국

1주차를 마무리하며...업무와 같이 할 수 있겠지 싶었는데, 강의의 내용이 알차서 생각보다 힘들었습니다.코드를 따라치며 수강했지만... 사실 제대로 이해 못하고 따라만 친 부분도 상당히 있었는데,다시 내가 직접 리펙터링 해가며 따라가야지 다짐을 하며 1주차 발자국을 남깁니다.강의 정리클린코드란 무엇인가라는 질문으로부터 강의가 시작됩니다.좋은 코드란 무엇일까요?저는 강의의 내용을 듣고 이렇게 정리했습니다.좋은 글과 마찬가지로 클린코드는 잘 읽히는 코드한 문장 한 문장에 주제가 또렷해 전달하고자 하는 내용을 읽는 이에게 정확하게 전달하는 코드강의에서 중간중간에 선조와 후손이라는 용어를 사용하십니다. 내가 쓰는 코드가 꼭 내것만은 아니라는 점을 계속 상기시켜주시면서, 공용의 코드를 어떻게 다뤄야할지는 말씀해주시는게 많이 도움이 될 것 같습니다.추상화추상이란 '사물을 정확하게 이해하기 위해서 사물이 지니고 있는 여러 가지 측면 가운데서 특정한 측면만 가려내어 포착하는 것'을 말합니다.'비가온다' 라는 표현을 '대기내에 수증기가 응결하여 물방울로 변하고, 이 물방울이 중력에 의해서 지표면으로 떨어진다'라고 친구한테 말하면 갸우뚱할 것입니다.이처럼 추상은 중요한 정보만 남겨 전달함으로써 상대방의 이해를 돕는 기능을 하는걸 실생활에서도 볼 수 있습니다.코드에서의 추상화그러면 코드에서는 어떻게 중요한 내용만 가려내고, 구체적인 내용은 숨겨 읽는 이의 이해를 도울 수 있을까요?강의에서 나온 내용들 중 기억에 남는 방법들을 나열해봅니다.코드 블럭내에서 추상화 레벨을 통일하기객체에 적절한 역할과 책임을 부여하고 메시지를 통한 협력을 하는 구조로 만들기SOLID 원칙을 생각해가며 코드를 작성하기객체지향의 다양한 기법을 활용하기상속과 조합Value Object컬렉션으로 포장한 일급컬렉션 사용하기Enum다형성  그리고 읽는 사람을 배려한 다양한 코드 작성법을 나열해봅니다.변수, 메서드 추상화를 통해 이름 짓기매직 넘버, 매직 스트링 상수화하기공백 라인 활용해 블럭내 코드 분간해주기조건문에 부정어 연산자 `!`줄이기

백엔드클린코드

당황한 수달

[인프런 워밍업 클럽 3기] PM/PO 1주 차 발자국

 스터디 완주를 위한 첫걸음: ‘발자국’을 남기며스터디 완주를 위해 학습 기록을 남기는 것도 중요하지만, 먼저 ‘발자국’이란 무엇이며, 왜 남기게 되었고, 어떤 이유로 이 강의를 선택했는지를 이야기해 보고자 합니다.  ‘발자국’이란 무엇인가?저는 시작하는 PM/PO들에게 알려주고 싶은, 프로덕트의 모든 것이라는 인강을 수강하며, 인프런 워밍업 스터디에 참여하고 있습니다.이 스터디에서는 완주를 위한 조건으로 ‘발자국’이 있는데, 이는 배운 내용을 바탕으로 작성하는 학습 일지이자 회고입니다.  왜 이 강의를 선택했는가?이전에 시작하는 PM/PO들을 위한 역할과 전문성이라는 인프런 밋업에 참여했고, 김민우 튜터님의 강연이 깊은 인상을 남겼습니다.마침 이 강의로 스터디가 개설되었고, 함께 학습하며 성장하기에 좋은 기회라고 생각되어 수강을 결정했습니다.  1주 차 동안 무엇을 배웠는가?가이드에서는 강의 내용을 요약하라고 했지만, 이미 많은 분들이 정리해 주셨을 것 같아😅저는 1주 차 동안 제 머릿속에 남은 것을 공유해 보려 합니다. 강의의 대상이 ‘시작하는 PM/PO’인 만큼, 이미 알고 있던 내용도 있었지만, 한편으로는 깊게 고민하지 못한 부분도 많았습니다.가장 인상적이었던 것은 “PM이란 무엇인가?” 라는 질문에 대한 튜터님의 정의였습니다. Product Manager란?valuable, usable, feasible, viable(VUFV)한 제품을 만들기 위해고객, 데이터, 인더스트리, 그리고 우리 사업에 대한 전문성을 팀에 기여하는 역할 저는 가끔 면접에서 이 질문을 받은 적이 있었는데, 인터넷에서는 명확한 정의를 찾기 어려워 정리하는 데 어려움을 겪고 직접 문의를 드리기도 했습니다.튜터님께서 이 질문에 대해 깔끔하게 정의해 주신 덕분에, 앞으로 면접에서 자신 있게 답변할 수 있을 것 같습니다. 또한, PM의 전문성에 대해서도 새로운 시각을 가지게 된 계기가 되었습니다.어떤 사람들은 PM을 ‘잡무 매니저’라고 하며 전문성이 없다고 평가하기도 합니다.하지만 잡무는 제품을 위해 필요하다면 해야 하는 부분이고,진정한 PM이라면 결국 VUFV한 제품을 만들기 위해 끊임없이 고민하고 발전하는 사람이라는 점을 되새길 수 있는 시간이었습니다. 앞으로도 VUFV한 제품을 만들 수 있는 PM이 되도록 계속해서 고민하고, 발전하는 사람이 되도록 노력해야겠습니다. 😊(솔직히 고백하자면, VUFV는 제가 만든 줄임말입니다🤣)  다음 주 학습 계획은?다음 주 강의 내용은 고객에 대한 전문성을 쌓는 강의들로 구성되어 있습니다.스케쥴에 맞춰 듣고, 현재 맡고 있는 제품에 대해 반영해 보는 기회로 삼아보고 싶습니다.  

기획 · PM· PO김민우튜터인프런워밍업클럽PMPO

바커스

챗GPT 지브리 스타일 사진 변환 프롬프트 예시

 얼마 전 SNS에서 “내 사진을 지브리 애니메이션처럼 바꿔봤어요”라는 게시물을 보고 놀랐습니다.진짜 지브리 스튜디오에서 그려준 것처럼 생생하고 감성적인 이미지였거든요.알고 보니 이게 전부 챗GPT에서 지브리 사진 변환으로 만들어진 결과물이더라고요. 이게 가능한 이유는, GPT-4 Turbo 모델이 기본적으로 이미지 인식과변환에 특화된 기능을 제공하기 때문이에요.과거에는 이런 걸 하려면 미드저니 같은 전문 AI 이미지 생성 도구를 써야 했는데,이제는 챗GPT 하나로 가능하다는 점에서 놀랍죠.너무 단순하면서도 의외죠?AI가 내 사진을 지브리 사진 스타일으로 바꿔준다고요? 이제 그 사용법, 직접 보여드릴게요.챗GPT로 지브리 스타일 사진 만드는 법 (사용법 총정리)제가 실제로 사용해 본 경험을 바탕으로 설명드릴게요.어렵지 않습니다. 아래 5단계를 따라 하시면 됩니다.1단계: 챗GPT 접속https://chat.openai.com에 접속해서 로그인하세요.무료 사용자도 가능하지만, 이미지 처리 기능은 유료 사용자(GPT-4)에게 우선적으로 제공됩니다.유료사용자는 sora 홈페이지에서도 가능합니다.더 고해상도 이미지 결과물 나옵니다. 2단계: 이미지 업로드채팅 입력창 옆에 ‘+’ 버튼을 누르면 사진을 업로드할 수 있어요.자신이 지브리 스타일로 바꾸고 싶은 사진을 선택하면 됩니다. 3단계: 프롬프트 입력“이 사진을 지브리 스타일로 변환해줘” 또는 “지브리 애니메이션 느낌으로 그려줘”라고입력해 주세요. 간단하게 말해도 AI가 알아서 이해합니다. 4단계: 결과 확인몇 초 안에 지브리 감성이 물씬 풍기는 이미지가 생성됩니다.인물의 눈은 크고 선명하고, 배경에는 따뜻한 색감의 애니메이션 효과가 적용돼요. 5단계: 추가 요청“눈동자를 더 반짝이게 해줘” “배경을 하늘로 바꿔줘”처럼 원하는 대로 수정도 가능합니다.처음엔 이게 진짜 될까? 싶었는데, 직접 해보면 정말 감탄하게 돼요. 6단계:프롬프트 팁이 궁금하신면 아래 링크에서 확인가능합니다.지브리 스타일 사진 변환 팁 보러가기 추가적으로 저작권관련 사항이나 상업적 대안으로 사용 방법이 궁금하시면 아래 링크를 참조하세요. 챗GPT 지브리 스타일 하는법 저작권 정리(프롬프트예시)  

AI · ChatGPT 활용지브리스타일사진변환챗GPT지브리변환AI지브리변환챗GPT지브리하는법chatgpt지브리하는법

kailis

발자국 4주차: 그래서 우리는 왜 귀찮음을 이겨내야 하는가

  그래서 우리는 왜 귀찮음을 이겨내야 하는가 마지막 주간. 나는 이번 강의를 전반적으로 들으면서 문득 떠오른 그림이 있었다.  수학에서 함수를 이야기할 때 가장 먼저 나오는 그림이다.  우리가 테스트를 하는 대상은 결국 추상화된 로직 안쪽에 대한 구체적인 결과물이다.  테스트에서 가장 중요한 것은 I/O. 그리고 연계성. 여기에서 여러가지 박스가 중첩되어 있는 게 프로그램이고,  우리가 만든 이 프로그램에서 a라는 것이 정확한 f(a)를 보장하는가? 에 대한 물음표를 컴파일 타임에서 해소해 주는 것. 즉 I/O를 얼마나 촘촘하게 필터링할수 있을지에 대한 이야기이다.  Mockist가 여기서 발언할 수 있는 여지가 있다고 생각한다.  위에 있는 박스가 어떤 것을 뱉든 a가 막을 수 있는 테스트코드로 막으면 되잖아?  난 결국 a에 대한 모든 케이스를 전부 테스트했으니까. 연계에 있어서 굳이 힘을 뺄 이유가 있겠느냐는 패러다임이다.  나는 강의에서 말씀하신 것과 비슷한 시야를 가지고 있는 것 같은데 ㅎㅎ  결국 인간이란 실수할 수 있는 동물이지 않나... 하는 생각을 한다. 어떠한 아웃풋이 나올지 모르니까 그걸 막으려고 테스트를 한다.어떠한 인풋이 나올지 모르니까 그걸 막으려고 테스트를 한다.  이 관점에서는 확실히 시나리오를 짜는 일이 유리해 보이니까.나올 수 있는 케이스를 조금 더 촘촘히 만들고 막는 것이 Classicist의 접근 방향이지 않을까.... 하는 생각이 들었다.  근데 귀찮잖아. 많은 테스트는 귀찮잖아.  아니, 테스트는 귀찮잖아!  아, 귀찮아......   음... 테스트 작성은 귀찮다.  마지막 강의의 말미에서도 이야기하는 만큼, 선생님도 "귀찮음"을 이겨내고 테스트를 작성하신다고 했다.  결국은 장기적으로 유지보수가 될 프로그램에 대한 마음의 확신. 과감한 리팩토링을 가능하게 하는 것.  사실 이런 말이 무의미할 수도 있다.  이 강의를 듣는 것 자체가 테스트가 중요하다는 것을 알고 있어서라고 생각하니까. 그러면 우리는 이 귀찮음을 어떻게 이겨내는가?  그래서, 실행  강의나 책의 가장 중요한 핵심은 제목에 있다고 생각한다.  강의의 이름을 다시 한번 돌이켜 보자.  Practical Testing: 실용적인 테스트 가이드 결국 Practical.  강의에서도 계속 강조하는 만큼 결국 가장 중요한 것은 실무에서의 활용이다. 어떻게 활용할 수 있을까? 무엇이 귀찮음을 이겨내도록 할까? 무엇에 대한 내용은 강의에서 많이 들었다. 따라서 나는 어떻게로 접근해 보고자 했다.   귀찮음을 어떻게 이겨낼까?   많은 자기계발서들은 귀찮음을 이겨내는 방법에 대해서 이야기한다.  의지력을 발휘하는 법, 언제나 열정을 불어넣는 법, 시간과 체력을 관리하는 방법.... 등등등. 난 이게 다 무의미하다고 생각하는 사람이다.  결국 Do에 있어서의 방법론이 귀찮음을 누른다고 생각하니까.  나는 하기 싫은 일이 있을 때마다 하고 싶어질 때까지 아주 잘게 쪼갠다.  그럼 테스트에서도 이걸 적용하려면? 어떻게 해야할까? 나는 먼저 <Sudo 시나리오 짜기>로 접근해 보고자 했다.  우리는 f(a)라는 함수를 테스트하려고 한다. 개별에 대한 단위 해피 테스트, 엣지 테스트 같은 것들을 리스트에 포함해 보면 좋겠다. 그리고 연계 시의 단위 해피 테스트, 엣지 테스트 같은 것들을 리스트에 포함해 보면 좋겠다.  오. 나는 이제야 테스트 작성이 좀 더 빠르게 접근할 수 있는 것으로 보인다.  이것만 함수로 만들면 되잖아? a에 대해 들어올 수 있는 값은 null일 수도, 숫자일 수도, 문자열일 수도, 객체일 수도 있다. 우리가 만약 string이라고 했다면, 숫자와 null에 대해서 필터링을 할 방법을 다 적어보면 되겠다. 강의에서는 이 방법으로 ParameterizedTest를 이야기했다. @ParameterizedTest @NullAndEmptySource @ValueSource(strings = {"Hello", " ", "123", "!@#"}) @DisplayName("입력값이 null 또는 빈 문자열이면 안 된다.") void shouldNotAllowNullOrEmptyString(String input) { // given String result = f(input); // then assertNotNull(result); assertFalse(result.isEmpty()); } f(a)로 만들어질 수 있는 값도 동일하다. null일 수도, 숫자일 수도, 문자열일 수도, 객체일 수도 있다. 우리가 만약 객체라고 했다면, 그 객체가 만들어낸 값이 동일한지를 찾아보면 되겠다.  @Test @DisplayName("출력값은 null이 아니어야 한다.") void shouldNotReturnNull() { // given String input = "hello"; // when String result = f(input); // then assertNotNull(result); } @Test @DisplayName("출력값은 예상한 문자열과 일치해야 한다.") void shouldReturnExpectedString() { // given String input = "hello"; // when String result = f(input); // then assertEquals("HELLO", result); } @Test @DisplayName("출력값은 모두 대문자로 변환되어야 한다.") void shouldConvertToUpperCase() { // given String input = "hello"; // when String result = f(input); // then assertTrue(result.matches("[A-Z]+")); } 같은 식으로 말이다.  여기서 아주 촘촘하게 테스트를 짠다면 좋겠지만 인간은 언제나 놓치는 존재니까.  그래도 아무것도 없는 백지에서 짜는 것보다 훨씬 더 촘촘할 것이고,  조금 더 접근하기가 쉬워지지 않을까. 적어도 나는 그랬다.   테스트가 서비스 운영에서 어떤 위치를 가질 수 있는지 이야기해보는 시간이었으니,  나도 정리와 함께 활용 방법을 이야기해보고 싶어 여러 이야기들을 종합적으로 해 봤다. 이제 오늘을 끝으로 인프런 워밍업 클럽 스터디 3기는 막을 내리게 된다. 회사 일, 이직 면접, 스터디를 병행하며 했던 만큼 더욱 뿌듯함이 큰 것 같다.  (이 병행을 이유로 우수 수강생은 노리기 힘들 것 같아서 조금 아쉽기는 했다. ㅎㅎ 아직 수퍼맨은 아닌 걸로...) 사실 테스트 코드에 대한 배움을 가장 중점으로 가지고 들어왔지만, Readable Code 수강을 하면서 예상치 못한 배움들이 등장했다.아는 것이라고 여기는 게 얼마나 바보같은 일인지 조금 더 절감하는 시간이었다. 수강은 이번에 막을 내리지만, 앞전 언급했던 것처럼 결국 가장 중요한 것은 실행이라는 것을 알고 있다.  실행을 토대로 내일부터 조금씩 테스트를 작성해 보는 시간을, 실제로 시간을 이유로 하지 못하더라도 어떠한 테스트 케이스가 나올 수 있는지에 대한 정리를 하고서 테스트를 하는 습관을 들여 보고자 한다.  3월은 짧았는데 어쩐지 워밍업 클럽은 길었던 느낌.  모두 고생하셨습니다.    

lkwo

워밍업 클럽 4기 BE 클린코드 4주차 발자국

4주차를 마무리하며...워밍업 클럽의 마지막 주에는 테스트 강의를 마무리하는 시간이 였습니다. 그리고 마지막 점검을 통해 제가 놓치고 있는 테스트의 시야를 넓혀주는 시간을 보냈습니다. ^^✅ 하나의 테스트에는 하나의 목적의 검증만!테스트 코드는 문서의 역할을 하기도 합니다.이를 글쓰기 관점에서 보았을때, 한문단에 하나의 주제만 쓰는것이 전달하기에 명료한 것과 같이 볼 수 있습니다.반복문과 분기문(논리구조)은 되도록 안 쓰는 테스트가 좋습니다.✅ 테스트에 대한 환경은 완벽하게 제어제어 가능한 영역과 불가능한 영역을 구분하여 테스트 코드를 작성하는 것이 좋습니다.외부 시스템 같은 경우 mock객체를 사용합니다.✅ 테스트 환경의 독립성을 보장하나의 테스트에서는 하나의 행위에 대한 내용으로 작성하는 것이 좋습니다.두가지의 행위가 혼합되어진 테스트인 경우에는 읽는이가 논리적으로 한번 더 생각해야 하고, 주 목적에 맞지 않는 이유로 테스트의 목적을 보장할 수 없게됩니다.✅ 테스트 간 독립성을 보장테스트 코드간은 서로 독립되어 동작해야 합니다.테스트 간에 순서가 생기면 안됩니다.공유 변수나 영향을 주는 persistence는 제거!사용해야하는 상황에서는 dynamic test 사용합시다.✅ Test Fixture 구성Fixture: 고정물, 고정되어 있는 물체, 테스트를 위해 원하는 상태로 고정시킨 일련의 객체(주로 given절에서 사용)@BeforeAll, @BeforeEach, @AfterEach를 통해 매 테스트에 사용할 객체를 고정시키는 것은 각 테스트의 독립성에 저해됩니다.무분별한 setup은 정보의 파편화로 인해 가독성에 저해됩니다.@BeforeAll, @BeforeEach, @AfterEach를 사용할 때는 다음과 같은 내용을 확인합시다.각 테스트를 이해하는데 아예 몰라도 되는 내용인가?수정해도 모든 테스트에 영향을 주지 않는가? ✅ Test Fixture 클렌징@AfterEach에 persistence layer의 repository의 delete메서드 실행합니다.@Transactional 사용하면 자동으로 rollback을 해줍니다만, 비즈니스 레이어에 트랜잭션에 대한 부분을 확인하기 어렵다는 사이드 이펙트가 있습니다.✅ @ParameterizedTest여러가지 파라미터(데이터)에 대해서 테스트를 진행하고 싶을 때 사용합니다.https://inf.run/7Vw48✅ @DynamicTest시나리오 테스트를 할 때 유용하게 사용할 수 있습니다.단계별로 행위와 결과를 검증합니다.https://inf.run/9nLpf✅ 테스트 수행도 비용이다각 테스트의 환경을 통일하여 테스트에 드는 비용을 줄일 수 있습니다.추상클래스를 상속받아서 테스트 코드가 돌아가도록 하면 환경을 통합해서 시간을 아낄 수 있습니다.상위클래스에 테스트에 필요한 빈들을 한번에 주입받아 여러번 spring 플레임 워크가 초기화하는 횟수를 줄일 수 있습니다. ✅ Private Method Testprivate method test는 할 필요가 없습니다.private method를 테스트 해야할 시점은 객체를 분리할 시점으로 볼 수 있습니다.✅ 테스트에서만 필요한 코드?생성자나 빌더와 같이 테스트에서만 사용되는 코드는 최소한으로 허용합니다.미래시점에 객체에 가져도되는 행위라고 생각이 되는경우 부담없이 사용합니다.✅ 시간. 시간. 시간시간이라는 요소는 개발자에게 선택을 강요합니다.한정된 시간에서 올바른 테스트를 작성하기 위해서는 테스트를 추론하고, tdd가 익숙해질 때까지 반복이 필요합니다. 요구사항에 매몰되기 보다는, 어떤 케이스로 코드를 검증할 수 있을지 한번 생각해보는 훈련이 필요합니다.도구를 빠르게 효율적으로 적재적소에 활용하기 위해서는 많이 사용해봐야 합니다.마지막 중간 점검그간 진행해오던 과제에 대해서 내가 제일 신경 쓴 부분은 중복제거, 깔끔해보이는 코드 였습니다. 이 생각이 잘못되었다는걸 이번 Day18 미션 리뷰에서 알 수 있었는데요. 중복제거나 깔끔해보이는 코드는 따라오는 효과이지, 그것이 목적이 되면 안될거라는 생각이 들었습니다. 리뷰에서 우빈님께서 하신 말씀 핵심은 중복 제거가 아니다. 도메인이다. 를 머리에 새겨넣어야겠습니다.워밍업 클럽 회고처음으로 여러명이 모여서 하나의 주제를 공부하는 시간을 보냈습니다. 짧다면 짧은 한달이지만, 응축되게 클린코드와 테스트 코드를 입문하기에 좋은 시간이 였습니다. 이 기간 동안 클린코드와 테스트 코드 짜는 저의 실력이 비약적으로 높아지지는 않았지만, 왜 필요하고 왜 꾸준히해야 하는지 목적의식을 심어주는 뜻깊은 시간이 되었습니다. 계속 이를 이어가고자.. 이 워밍업 클럽이 끝나고 따로 모여 스터디 진행을 신청했는데, 앞으로의 저의 생각과 코드에 많은 발전이 있기를 ...! 그동안 인프런 워밍업 클럽을 진행해주시느라 신경을 많이 써주신 박우빈 선생님과 인프런에 감사드리며 인프런 워밍업 클럽 "완"을 외치겠습니다!

웹 개발테스트코드클린코드

당황한 수달

[인프런 워밍업 클럽 3기] PM/PO 4주 차 발자국

이번 주 강의(시작하는 PM/PO들에게 알려주고 싶은, 프로덕트의 모든 것)에서는 성과를 내는 제품을 만들기 위한 Product Delivery와 성장 전략인 Product Growth를 중점으로 다뤘습니다.  4주 차 동안 무엇을 배웠는가?강의 전에는 가설을 세우고 솔루션을 검증하는 방법은 알고 있었지만, 정작 가설을 어떻게 세워야 하는지에 대한 기준이 없었다는 점을 깨달았습니다. 그래서 아이디어 중심으로 접근하는 경우가 많았습니다.이번 강의를 통해 Value, Usability, Viability, Feasibility라는 네 가지 관점에서 가설을 세우는 법을 배운 것이 가장 인상 깊었습니다. 또한, 답을 도출할 때 큰 범위에서 접근하기보다 작은 범위에서 해결하는 것이 훨씬 효과적이라는 점을 다시 한번 실감했습니다.그동안 A/B 테스트가 정답이라고만 생각했지만, 이번 강의를 통해 그 틀에서 벗어나 본질적인 문제 해결에 집중하는 것이 중요하다는 걸 깨달았습니다. 결국, 이 제품이 제공하는 궁극적인 가치가 무엇인가를 고민하는 것이 문제 해결의 핵심이라는 점을 배웠습니다.  4주 차 회고벌써 4주 차 마지막 회고입니다. 온라인 스터디였지만, 바쁜 일정 속에서도 인강을 꾸준히 듣고 완강할 수 있었다는 점이 뿌듯합니다.특히, 튜터님의 실시간 Q&A와 개인 커리어 상담이 인상적이었습니다. 단순한 강의 제공을 넘어, 관련 자료를 지속적으로 공유해 주시고 학습을 이어갈 수 있도록 도와주신 점이 감사했습니다. 덕분에 단순히 강의를 듣는 것을 넘어, 프로덕트에 대해 깊이 고민하는 시간을 가질 수 있었습니다.이런 기회가 또 있다면 꼭 다시 참여하고 싶을 정도로 매우 추천합니다!

기획 · PM· PO김민우튜터인프런워밍업스터디PMPO

10

단위 테스트 작성법 그리고 Mock - 인프런 워밍업 클럽 3기 백엔드 코드✨

👣 발자국 책갈피인프런 워밍업 클럽 3기 백엔드 코드 발자국 1주차인프런 워밍업 클럽 3기 백엔드 코드 발자국 2주차인프런 워밍업 클럽 3기 백엔드 코드 발자국 3주차인프런 워밍업 클럽 3기 백엔드 코드 발자국 4주차✅미션PR 책갈피[미션 Day 2] 추상과 구체 예시 작성[미션 Day 4] 리팩토링 & SOLID 원칙[미션 Day 7] 리팩토링 연습[미션 Day 11] 단위테스트 작성[미션 Day 16] 레이어드 아키텍처 특징 및 테스트 작성법[미션 Day 18] Mock 어노테이션 종류 및 차이점 & BDD 패턴 매치⏰ 벌써 1분기 끝...약 한 달간의 인프런 워밍업 클럽 백엔드 코드 3기 여정이 끝났다.올해는 유난히 바빠서 그런지, 시간이 유독 더 빨리 가는 것 같다. 벌써 1분기가 끝났다는 게… 믿기지 않는다. 😂우빈님께서는 온라인 세션 때 시간이 빨리간다는 농담을 해주시곤 한다.우빈님의 지인이 ‘시간이 너무 빨리 가서 곧 크리스마스 트리를 설치해야겠다’고 하셨던 말씀이 기억에 남는다. ㅋㅋ 🤣 (맞나? 이게? 자세히 기억은 아나지만..)아무튼! 마지막 주차 최종 점검 온라인 라이브 세션을 마지막으로 스터디를 완주하였다. 👏기대하고 기다리던 코드 리뷰를 다시 받게 되었다!✨ 두번째 우빈님의 세심한 코드 리뷰이번 코드 리뷰는 작성한 단위 테스트 코드에 대한 리뷰를 받았다.중간점검 때 받았던 리팩토링 코드 리뷰보다는 과제가 다소 정형화(?) 되어 있어서 공통 피드백이 많긴 했다.다시 한번 우빈님의 세심한 리뷰에 놀랐다. 😮🔗 Github PR 링크단위테스트 작성 PR1⃣ 사용자 입력에 대한 테스트 방법이건 내가 PR에 궁금했던 질문 중 하나였다. 🧐프로덕션 코드를 수정하면 안된다는 제약을 걸고, 테스트 코드를 작성하려고 했기 때문에..!사용자 입력을 받는 Scanner에 대한 테스트는 어떻게 하는지 궁금했다.🧪️ 테스트 하려고 했던 코드public class InputHandler { private static final Scanner SCANNER = new Scanner(System.in); ...(중략)... }✏️ 우빈님 리뷰Q. 프로덕션 코드 수정 없이 사용자 입력 테스트가 가능할까요..? 🤔 (Scanner 클래스를 외부세계로 분리하면 가능할 것 같긴합니다..) 입력에 대한 테스트도 가능하면 전체 통합테스트도 가능할 것 같습니다!!! A. Scanner 때문에 어렵긴 하죠. InputStream을 생성자로 받는 형태로 변경하고, Scanner를 생성해주는 방식이라면 가능할 겁니다. 🤔 돌아보기역시, 프로덕션 코드를 수정하지 않으면 테스트가 어렵다는 말씀을 주셨다.우빈님 리뷰를 반영하여 프로덕션 코드 부분을 InputStream을 생성자로 받는 형태로 리팩토링해봐야겠다.2⃣ 테스트 커버리지에 대한 우빈님의 관점리뷰 신청 시에, "테스트 커버리지의 집착"에 대해 언급을 했었는데..세심한 우빈님께서 포인트를 짚어주셨다...!! 🥹✏ 우빈님 리뷰A. 연습 시에 커버리지를 극한까지 올리는 데에 집중해보는 것 -> 👍 그러나 실무에서는 '주어진 시간 안에' 중요도가 높은 순으로 테스트를 할지 말지를 결정해야 합니다. 물론 전부 다 할 수 있으면 best 겠죠 :) 🤔 돌아보기실무에서의 테스트 커버리지에 대한 관점을 말씀주셨다...테스트 커버리지를 높이는 것도 중요하지만, 비즈니스 우선이라는 점을 반드시 인지하자.테스트 코드 작성 시, 중요도를 따져보는 연습을 해봐야겠다.그리고 실무에서의 커버리지에 대한 집착은 지양하도록 하자.대신, 사이드 프로젝트에서는 커버리지를 극한까지 끌어올리는 연습에 집중해보는 것도 좋겠다.3⃣ 한 눈에 들어오게끔 'given' 절 작성하기다음 코드는 given 절에 선언한 컬렉션 변수가 너무 길어서 private 메서드로 분리한 형태이다.@DisplayName("좌석 패스로 기간과 타입이 동일한 사물함 패스를 찾는다.") @Test void findLockerPassBy() { // given List<StudyCafeLockerPass> list = lockerPassList(); StudyCafeLockerPasses lockerPasses = StudyCafeLockerPasses.of(list); ...(중략)... } private List<StudyCafeLockerPass> lockerPassList() { return List.of( StudyCafeLockerPass.of(StudyCafePassType.FIXED, 4, 11000), StudyCafeLockerPass.of(StudyCafePassType.WEEKLY, 4, 17000), StudyCafeLockerPass.of(StudyCafePassType.FIXED, 12, 11000), StudyCafeLockerPass.of(StudyCafePassType.FIXED, 4, 18000), StudyCafeLockerPass.of(StudyCafePassType.HOURLY, 8, 11000), StudyCafeLockerPass.of(StudyCafePassType.FIXED, 10, 11000) ); }✏️ 우빈님 리뷰A. lockerPassList()가 private 메서드라 list가 무엇인지 한 눈에 잘 들어오지 않는다. 어차피 정해진 리스트라면, 상단에 상수로 관리하면 어떨까? 네이밍도 list -> allLockerPasses로 "모든" 사물함 패스 라는 의미를 주면 모든 사물함 패스가 존재할 때, 내 좌석권에 맞는 사물함 패스를 찾는다는 내용으로 변수명을 변경하면 좀 더 이해하기 쉬울 것 같다. 🤔 돌아보기당시에 완전 뜨끔했던 리뷰였다.. 💯내가 작성한 코드를 보니 메서드도 메서드인데 왜 변수명을 저렇게 작성했을까?라는 의문이 든다. 🤦‍♂테스트 코드의 given 절은 중복 제거보다도 '한눈에 들어오는 것'이 더 중요하다고 하셨다.읽는 사람의 '뇌 메모리'를 덜 쓰게끔 given 절을 설계하는 연습이 필요해 보인다.리뷰를 바로 반영하여 아래의 코드로 리팩토링 했다. ♻private static final List<StudyCafeLockerPass> ALL_LOCKER_PASSES = List.of( // 👍 상수로 추출 및 네이밍 변경 StudyCafeLockerPass.of(StudyCafePassType.FIXED, 4, 11000), StudyCafeLockerPass.of(StudyCafePassType.WEEKLY, 4, 17000), StudyCafeLockerPass.of(StudyCafePassType.FIXED, 12, 11000), StudyCafeLockerPass.of(StudyCafePassType.FIXED, 4, 18000), StudyCafeLockerPass.of(StudyCafePassType.HOURLY, 8, 11000), StudyCafeLockerPass.of(StudyCafePassType.FIXED, 10, 11000) ); @DisplayName("좌석 패스로 기간과 타입이 동일한 사물함 패스를 찾는다.") @Test void findLockerPassBy() { // given StudyCafeLockerPasses lockerPasses = StudyCafeLockerPasses.of(ALL_LOCKER_PASSES);4️⃣ 전역적인 기능을 Stub시, 주의 하기다음은, 엑셀에 있는 패스권 목록을 가져오는 부분을 'mocking'한 부분이다.@DisplayName("파일을 읽어서 좌석 패스를 가져온다.") @Test void getSeatPasses() { try (MockedStatic<Files> mockedFiles = mockStatic(Files.class)) { // given mockedFiles.when(() -> Files.readAllLines(any())) .thenReturn(List.of( "WEEKLY,2,4000,0.0", "WEEKLY,12,120000,0.3", "HOURLY,4,6500,0.1" )); } }✏️ 우빈님 리뷰A. mockStatic으로 Files mocking 👍 다만, Files.readAllLines()를 stubbing하는 등의 전역적인 기능을 조작하는 것은 멀티스레드로 병렬 테스트를 수행할 때 문제가 될 수 있으므로 주의 필요 🤔 돌아보기mockStatic을 이용해서 작성한 코드가 병렬 테스트 수행 시, 테스트 코드가 깨질 수 있다는 사실을 처음 알게 되었다.간단한 테스트라면 괜찮을 수 있지만, 실무에서는 반드시 지양해야겠다.이번 코드 리뷰는 실무에서의 주의할 점에 대해 많이 언급해주셨다. ⭐️테스트 커버리지의 양면성 및 실무에서의 지양mockStatic의 병렬 테스트 시, 사이드 이펙트 발생단순히, 테스트 코드를 많이 작성하는 것보다 중요도 높은 혹은 의미있는 테스트 코드를 작성하려고 노력해야 겠다. ✨"이 글이 우빈님께 닿지는 않겠지만..😅우빈님! 감사드립니다.🙇‍♂"💡 자기만의 언어로 키워드 정리하기섹션 7. Mock을 마주하는 자세Test Double, Stubbing1⃣ Dummy아무것도 하지 않는 깡통 객체단순히 인자를 채우기 위해 사용되며, 호출되지 않음class DummyUser implements User { @Override public String getName() { return null; // 의미 없는 값 } }2️⃣ Fake단순한 형태로 동일한 기능은 수행하나, 프로덕션에서 쓰기에는 부족한 객체 (ex. FakeRepository)class FakeUserRepository implements UserRepository { private Map<Long, User> users = new HashMap<>(); @Override public User findById(Long id) { return users.get(id); } public void save(User user) { users.put(user.getId(), user); } }3️⃣ Stub테스트에서 요청한 것에 대해 미리 준비한 결과르 제공하는 객체, 그 외에는 응답하지 않는다.특정한 고정된 값을 반환하는 객체테스트에서 정해진 응답이 필요할 때 사용class StubUserRepository implements UserRepository { @Override public User findById(Long id) { return new User(id, "stub_user"); } }4️⃣ SpyStub이면서 호출된 내용을 기록하여 보여줄 수 있는 객체, 일부는 실제 객체처럼 동작시키고 일부만 Stubbing 할 수 있다.메서드 호출 여부, 호출 횟수 등을 검증하는 데 사용class SpyEmailSender implements EmailSender { private int sendCount = 0; @Override public void sendEmail(String message) { sendCount++; } public int getSendCount() { return sendCount; } }5️⃣ Mock행위에 대한 기대를 명세하고, 그에 따라 동작하도록 만들어진 객체@Test void testMockExample() { EmailSender emailSender = mock(EmailSender.class); emailSender.sendEmail("test@example.com"); verify(emailSender).sendEmail("test@example.com"); // 호출 검증 }Stub과 Mock차이Stub는 상태 검증, Mock은 행위 검증Stub는 메서드가 특정 값을 반환하도록 설정하기 때문에, 반환한 값에 대한 검증을 한다.Mock은 특정 메서드가 정확히 호출되었는지 검증하는 역할을 한다.Stubbing이란?Mock의 행위를 지정하는 것, 즉 Mock 객체의 행동을 조작하는 것Mockito의 when, thenReturn 메서드를 활용하여 Stubbing 할 수 있다.@Mock, @MockBean, @Spy, @SpyBean, @InjectMocksSpy는 이해할 때 기능 중에 스파이가 있다(?)라고 기억하면 편하다 ㅎㅎ 😂BDDMockitoBDD 스타일의 Mockito 버전으로 given(), willReturn(), then() 등을 사용하여 직관적인 테스트를 작성할 수 있다.Classicist vs. MockistMockist : 모든 테스트를 mocking 위주로 하자라는 입장Classicist : 진짜 객체간의 협업을 통한 보장 (mocking을 무조건 하지말라는 건 아님)각각 객체에 대한 테스트가 잘 되도 협업 시에는 모르는 문제가 나올 수 있다. (A + B = AB? BA? C?)외부 시스템 로직이 있을 때는 mocking 처리하는 것이 좋다.섹션 8. 더 나은 테스트를 작성하기 위한 구체적조언테스트 하나 당 목적은 하나!테스트 코드 내부의 분기문이나 반복문처럼 고민을 요구하는 코드는 로직이 여러가지이기 때문에, 테스트 케이스가 여러개이다.테스트 케이스 별로 각각의 테스트 코드를 작성하자.완벽한 제어given 데이터를 만들 때 LocalDate.now(), LocalDateTime.now() 사용하지 않는 게 좋다.테스트 코드 실행 시마다 의도하는 바가 달라지기 때문에 영향을 끼친다.제어할 수 없는 값을 제어 가능하게 변경하자.테스트 환경의 독립성, 테스트 간 독립성공유변수, 연관관계가 있는 테스트코드는 지양하자.Test Fixturegiven절에 최대한 명시한다.메서드를 추출할 때 필요한 파라미터만 명시한다.별도의 data.sql 데이터를 추출하지 않는다.단위테스트 내에서 모두 표현한다.deleteAll(), deleteAllInBatch()@Transactional은 사이드 이펙트를 고려해서 클렌징해야한다.결국은 테스트도 비용이다... 아무리 h2 인메모리 DB를 사용한다지만 deleteAll()처럼 다수의 쿼리가 발생하면 테스트 비용이 증가한다.deleteAllInBatch() 벌크성으로 데이터를 클렌징하다.@Transactional와 deleteAllInBatch() 혼용해서 사용하는 것이 좋다.@ParameterizedTest, @DynamicTest@ParameterizedTest동일한 테스트를 다른 입력값으로 테스트 할 때 사용@ValueSource, @CsvSource, @MethodSource 등 다양한 소스로부터 테스트가 가능하다.@DisplayName("상품 타입이 재고 관련 타입인지를 체크한다.") @ParameterizedTest @CsvSource({"HANDMADE, false", "BOTTLE, true", "BAKERY, true"}) void containsStockType3(ProductType productType, boolean expected) { // when boolean result = ProductType.containsStockType(productType); // then assertThat(result).isEqualTo(expected); } private static Stream<Arguments> provideProductTypesForCheckingStockType() { return Stream.of( Arguments.of(HANDMADE, false), Arguments.of(BOTTLE, true), Arguments.of(BAKERY, true) ); } @DisplayName("상품 타입이 재고 관련 타입인지를 체크한다.") @ParameterizedTest @MethodSource("provideProductTypesForCheckingStockType") void containsStockType4(ProductType productType, boolean expected) { // when boolean result = ProductType.containsStockType(productType); // then assertThat(result).isEqualTo(expected); }@DynamicTest테스트를 실행할 때 동적으로 생성하는 방식@TestFactory를 사용한다.@DisplayName("재고 차감 시나리오") @TestFactory Collection<DynamicTest> stockDeductionDynamicTest() { // given Stock stock = Stock.create("001", 1); return List.of( DynamicTest.dynamicTest("재고를 주어진 개수만큼 차감할 수 있다.", () -> { // given int quantity = 1; // when stock.deductQuantity(quantity); // then assertThat(stock.getQuantity()).isZero(); }), DynamicTest.dynamicTest("재고보다 많은 수의 수량으로 차감 시도하는 경우 예외가 발생한다.", () -> { // given int quantity = 1; // when & then assertThatThrownBy(() -> stock.deductQuantity(quantity)) .isInstanceOf(IllegalArgumentException.class) .hasMessage("차감할 재고 수량이 없습니다."); }) ); }수행 환경 통합하기더 자주, 더 빠르게 수행하는 환경을 구축하자.공통 환경을 추출해서 통합 클래스를 만들어 서버 뜨는 횟수를 줄인다.private method test수행할 필요가 없다.욕망이 강하다면 객체 분리의 신호이다.테스트에서만 필요한 코드프로덕션 코드에 만들어도 되지만 최대한 보수적으로 생성섹션 9. Appendix지만 중요한 것들학습 테스트잘 모르는 기능, 라이브러리, 프레임워크를 학습하기 위한 테스트 코드여러 테스트 케이스를 스스로 정의하고 검증하는 과정을 통해 구체적인 동작과 기능을 학습Spring Rest Docs테스트 코드를 통한 API 문서 자동화 도구API 명세를 문서로 만들고 제공함으로써 협업을 원활하게 한다.👨🏻‍💻 미션 회고[미션 Day 16][미션 PR][미션 Day 16] 레이어드 아키텍처 특징 및 테스트 작성법1⃣ 레이어드 아키텍처 특징 및 테스트 작성법레이어드 아키텍처의 특징을 개념 위주로 정리하고, 레이어별 테스트 작성법은 예제 코드를 활용해 정리하였다.특히, 레이어별 테스트를 작성하는 과정에서 Test Fixture와 데이터 클렌징 개념을 함께 학습하며, 이를 예제 코드에 적용하였다.차후에 실무 및 사이드 프로젝트에서 레이어드 아키텍처를 직접 적용해 보며 응용해볼 계획이다.[미션 Day 18][미션 PR][미션 Day 18] Mock 어노테이션 종류 및 차이점 & BDD 패턴 매치2⃣ Mock 어노테이션 종류 및 차이점 & BDD 패턴 적용📌 Mock 어노테이션 종류 및 차이점Mockito의 주요 어노테이션(@Mock, @Spy, @InjectMocks, @MockBean, @SpyBean)의 차이를 자기만의 언어로 정리하였다.순수한 Mock 기반 단위 테스트와 Spring Context 기반 통합테스트에서 각각 어떤 어노테이션을 사용해야 하는지 이해하였다.📌 BDD 패턴 적용댓글의 주요 로직을 테스트하는 클래스 CommentTest을 작성하였다.각 테스트 케이스에서 댓글 도메인을 테스트 하기 위한 사용자와 게시글을 생성하는 코드를 @BeforeEach 절에 배치하였다.🏃 돌아보며..짧다면 짧고, 길다면 긴 인프런 스터디 여정을 드디어 완주했다. 👏👏👏(잠시나마, 한숨을 돌릴 수 있게 되었다. 😮‍💨)한 줄 평을 해보자면, "정말 너무 좋기만 했다."생각보단 업무와 병행하며 쉽지 않은 일정이긴 하지만!?내가 듣고 싶었던 강의의 강의료만 내고..강사님과의 네트워킹을 하며.. 스터디에 참여하고..단기간에 성장까지 경험할 수 있다면, "참여하지 않을 이유가 있을까?"🤔다음 기수 때도 기회가 된다면 지원을 해 볼 생각이다!!그리고! 주변에서 참여를 고민한다면!? 바로 적극 지지 해줄 것 같다.인프런 워밍업 클럽 스터디 만만세!! 🙌강사진과 운영진분들, 진심으로 고생 많으셨습니다. 감사합니다! 🙇‍♂[출처]인프런 워밍업 클럽 : https://www.inflearn.com/course/offline/warmup-club-3-be-code강의 : https://www.inflearn.com/course/readable-code-%EC%9D%BD%EA%B8%B0%EC%A2%8B%EC%9D%80%EC%BD%94%EB%93%9C-%EC%9E%91%EC%84%B1%EC%82%AC%EA%B3%A0%EB%B2%95/dashboard

백엔드인프런워밍업클럽백엔드3기발자국테스트코드실용적인테스트가이드다음에또봐요

10 2일 전
10

[인프런 워밍업 클럽 3기 - 백엔드 코드] 발자국 1주차

👣 발자국 책갈피인프런 워밍업 클럽 3기 백엔드 코드 발자국 1주차인프런 워밍업 클럽 3기 백엔드 코드 발자국 2주차인프런 워밍업 클럽 3기 백엔드 코드 발자국 3주차인프런 워밍업 클럽 3기 백엔드 코드 발자국 4주차✅미션PR 책갈피[미션 Day 2] 추상과 구체 예시 작성[미션 Day 4] 리팩토링 & SOLID 원칙[미션 Day 7] 리팩토링 연습[미션 Day 11] 단위테스트 작성[미션 Day 16] 레이어드 아키텍처 특징 및 테스트 작성법[미션 Day 18] Mock 어노테이션 종류 및 차이점 & BDD 패턴 매치🤔 인프런 워밍업 클럽 스터디 신청 계기나는 인프런 워밍업 클럽 스터디 신청하기 한 달 전에 스터디 과정에 쓰이는 로드맵 강의를 이미 완강하였다.그럼에도 불구하고 강의를 수강하기 전부터 우빈님의 팬(?)이였기 때문에 이메일을 받자마자 신청을 할 수 밖에 없었다.항해 플러스 8기 백엔드 과정을 앞두고 약 2주간 겹치는 구간이 있어 잠시나마 고민을 하긴 했지만?..복습 차원에서 미션과 발자국을 통해 학습내용을 더 딥하게 파고들어 성장하고자 신청하게 되었다.👣 발자국이란?인프런 워밍업 클럽에서 이야기하는 발자국이란 다음과 같다.주 1회, 일주일 동안 배운 내용을 바탕으로 남기는 학습 일기이자 회고이다.매주 강의를 요약하고 기록하면서 강의 내용을 제대로 이해했는지 확인하고, 보완할 부분을 찾는다.기록을 통해 배운 내용에 대해 메타인지 하는 시간을 가진다.나는, 이번 과정에서는 강의를 재수강하지는 않고섹션 별로 우빈님께서 제공해주신 키워드 정리를 통해 학습을 진행할 예정이다.💡 자기만의 언어로 키워드 정리하기섹션 2. 추상추상과 구체 : 이름 짓기 + 메서드 선언부 = 추상화 행위실제 코드를 작성하다보면 이름 짓기에 시간을 가장 많이 투자한다.이름 짓기의 중요성이 크기 때문에 반복되는 훈련을 통해 이름 짓기의 스킬 향상이 필요해 보인다. 추상화 레벨메서드를 추출하는 과정에서 외부셰계와 내부세계가 분리가 되고, 이 과정에서 추상화 레벨을 잘 고려해야한다.추상화 레벨이 동등해야 자연스럽게 코드가 읽힌다.[다른 추상화 레벨]public class Order { public void process() { paymentService.pay(); // 고수준 추상화 📈 System.out.println("결제 완료"); // 저수준 추상화 📉 } } [동등한 추상화 레벨]public class Order { public void process() { paymentService.pay(); // 고수준 추상화 📈 printCompletedPay(); // 메서드를 분리하여 고수준의 추상화를 동등하게 유지 ✅ } private void printCompletedPay() { System.out.println("결제 완료"); } } 매직 넘버 + 매직 스트링 : 상수 혹은 Enum을 활용하여 이름 짓기를 통해 가독성이 좋아지게 한다.섹션 3. 논리, 사고의 흐름뇌 메모리 적게 쓰기 (인지적 경제성)읽기 좋은 코드는 읽는 사람으로 부터 뇌를 편안하게 한다.반대로 읽기 힘든 코드는 읽는 사람의 뇌를 피곤하게 한다. Early return : Early return은 아래쪽 내용을 읽을 필요 없어 뇌 메모리 적게 쓰기에 효과적이다.사고의 depth 줄이기중첩 분기문가 중첩 반복문을 무조건 depth를 줄이기보다, 추상화를 통한 사고의 depth를 줄이는 것이 중요하다.사용할 변수는 가깝게 선언한다.공백 라인 : 공백 라인도 리팩토링 시 중요한 요소이다. 공백 라인이 존재하지 않은 코드는 읽기 어렵다.부정어부정연산자는 가독성을 해친다. 여러 번의 사고를 거치면서 코드를 읽어야 한다.한 번에 사고할 수 있도록 부정연산자를 제거하고 메서드로 추출했다면 이름 짓기를 적절하게 수정해야한다.해피 케이스, 예외 처리해피 케이스보다 예외처리에 더 신경써서 코드를 작성해야 한다.예외는 의도하지 않은 예외랑 의도한 예외가 있다.섹션 4. 객체 지향 패러다임객체, 협력과 책임, 관심사의 분리, 높은 응집도와 낮은 결합도협력과 책임 : 객체간의 협력, 객체가 담당하는 책임관심사의 분리 : 관심사에 따라 객체를 만들어 낼 수 있다.높은 응집도 : 특정한 관심사로만 이루어진 설계낮은 결합도 : 각 관심사끼리는 독립적이여야 한다.[낮은 응집도]public class OrderService { public Order createOrder(Product product) { // 주문 생성 로직 } public void sendEmail(Order order) { // 이메일 전송 로직은 OrderService의 관심사가 아니다. ❌ } } [높은 응집도로 해결]객체를 분리함으로써 응집도가 올라간다.public class OrderService { public Order createOrder(Product product) { // 주문 생성 로직 } } public class OrderEmailSender { // 객체를 분리하여 높은 응집도로 설계 ✅ public void sendEmail(Order order) { // 이메일 전송 로직 } } [높은 결합도]public class OrderService { private EmailService emailService = new EmailService(); // 높은 결합도 ❌ } [낮은 결합도로 해결]인터페이스와 DI를 활용하여 결합도를 낮춘다.public interface EmailSendable { } public class OrderEmailSender implements EmailSendable { } public class OrderService { private final EmailSendable emailSendable; public OrderService(EmailSendable emailSendable) { this.emailSendable = emailSendable; // 높은 결합도를 해결한다. ✅ } }객체끼리의 협력과 책임을 통해 프로그램을 만들 수 있다.가장 중요한 것은 관심사의 분리이고 높은 응집도와 낮은 결합도를 가진 설계를 가지는 것이 중요하다. getter/setter 자제하기, 객체에 메시지 보내기setter는 최대한 지양하는 것이 좋다.getter를 남발하는 건 객체를 존중하지 않는 것이며 무례하면서 폭력적인 행위이다.SOLID: SRP, OCP, LSP, ISP, DIP아래의 미션 Day 4 과정에 대한 내용을 통해 자세히 다룬다.DI/IoC섹션 5. 객체 지향 적용하기상속과 조합 : 상속은 결합도가 높기 때문에 조합을 활용하는 것이 더 좋은 설계이다.Value Object, EntityVO는 불변성, 동등성, 유효성을 보장해야하며 도메인의 개념을 추상화한 객체이다. (ex. Money 객체)Entity는 식별자가 존재한다. 식별자가 같으면 동등한 객체로 취급한다.일급 컬렉션 : 컬렉션을 Wrapping한 객체를 뜻한다. 컬렉션을 추상화하여 의미를 담을 수 있고, 가공 로직의 보금자리가 생긴다. (VO와 비슷하다.)Enum : 상수의 집합, 상수들에 대한 로직을 담을 수 있다.추상화와 다형성 활용하여 반복되는 if문 제거 -> OCP 지키기변하는 것 : 조건 & 행위 (구체)변하지 않는 것 : 조건을 만족하는가? / 행위를 수행한다. (추상)변하는 것과 변하지 않은 것을 구분해서 보는 훈련이 필요하다.숨겨져 있는 도메인 개념 도출하기 : 변경이 많이 일어날 것 같은 미래를 예견하고 이런 것을 도입해보면 어떨까? 하고 숨겨진 도메인 개념을 도출도메인 지식은 만드는 것이 아니라 발견하는 것이다.객체지향은 흉내내는 것이다.미래를 예견하고 도메인 개념을 도출해보자.[이메일 전송 개념에서 미래를 예견하고 숨겨진 도메인 개념을 도출]현재의 최선에서는 이메일만 발송하지만 미래에 카카오톡 알림톡이나 혹은 다른 알림에 대한 도메인 개념을 도출해보자public interface Sendable { // 미래를 예견하고 도매인 개념을 도출 ✅ void send(String message); } public interface EmailSendable extends Sendable { @Override void send(String message); }👨🏻‍💻 미션 회고 [미션 Day 2]미션 PR : https://inf.run/dmW3B1⃣ 추상과 구체 예시강의 내용에서도 계속 등장하듯이 추상과 구체는 이번 강의에서 가장 핵심이 되는 단어들이다.이 미션을 접했을 때, 단순히 추상과 구체에 대한 예시를 들기보다 개발적인 관점에서 추상과 구체에 대해 접근하려고 노력했다. (적절한 예시인지는 잘 모르겠으나..😂)해당 미션을 통해, 추상화가 단순히 쉽지 않다는 걸 느꼈으며 개발적인 관점에서도 더 읽기좋은 코드를 작성하기 위해선 추상화를 잘 해야겠다라는 생각이 들었다. [미션 Day 4]미션 PR : https://inf.run/1P5bV1⃣validateOrder 메서드를 읽기 좋은 코드로 리팩토링 하기해당 메서드를 봤을 때는 엄청 어지러웠다.. 🤣 본능적으로 빨리 리팩토링 하고 싶다는 생각이..추상화 기법들을 통해 도메인 객체를 분리하고 도메인 내부세계로 검증 로직을 구현하였다.객체 분리 : 일급 컬렉션으로 의미있는 Items의 객체로 분리하였고, Member라는 객체를 생성하여 customerInfo에 대한 검증 로직을 구현하였다.사고의 depth 줄이기, 부정 연산자 지양 : 중복 분기문과 부정 연산자를 포함한 조건식을 리팩토링하여 읽는 사람으로 하여금 뇌 메모리 적게 쓰게 끔 리팩토링했다.미션 PR에도 코멘트를 달았지만 도메인 레이어의 로깅이 하는 구현 부가 포함된 게 신경이 쓰인다.도메인 레이어에서 의존성을 주입받지는 않아 객체 분리가 맞는지 모호하다.[기존 코드]public boolean validateOrder(Order order) { if (order.getItems().size() == 0) { log.info("주문 항목이 없습니다."); return false; } else { if (order.getTotalPrice() > 0) { if (!order.hasCustomerInfo()) { log.info("사용자 정보가 없습니다."); return false; } else { return true; } } else if (!(order.getTotalPrice() > 0)) { log.info("올바르지 않은 총 가격입니다."); return false; } } return true; }[리팩토링 이후 코드]public class Order { private static final Logger log = Logger.getLogger(Order.class.getName()); private final Items items; private final Member member; private final int totalPrice; private Order(Items items, Member member, int totalPrice) { this.items = items; this.member = member; this.totalPrice = totalPrice; } public boolean validate() { if (items.isEmpty()) { log.info("주문 항목이 없습니다."); return false; } if (totalPrice <= 0) { log.info("올바르지 않은 총 가격입니다."); return false; } if (member.hasNotInfo()) { log.info("사용자 정보가 없습니다."); return false; } return true; } } 2⃣ SOLID에 대하여 자기만의 언어로 정리사실, 객체 지향에 대해 학습한 사람들 중 SOLID를 모르는 사람은 없을 것 이다.그치만 여기서의 포인트는 자기만의 언어이다.위에서도 강의에 대한 정리내용을 작성할 때 "💡 자기만의 언어로 키워드 정리하기" 라는 제목으로 작성하였다.학습한 내용을 온전히 내 것으로 만들기 위해선 반드시 나의 언어로 작성해야 내 것이 되고, 차후에 정리한 내용을 다시 봤을 때도 금방 기억을 더듬을 수 있을 것 이다.그래서 나는 텍스트 보다 코드로 봤을 때의 이해도가 더 빠르기 때문에 SOLID 원칙의 내용을 정리할 때 불필요한 텍스트를 줄이고 코드로 내용을 정리하였다.🏃 돌아보며..위에서도 언급했지만, 항해 플러스 백엔드 스터디 2개도 병행하고 있어 참여를 망설였지만.. 신청하기 잘한 것 같다.(2기가 마지막이 될까봐 조마조마 했던 1인.. 우빈님 감사합니다.. 🙇‍♂)온라인 밋업을 통한 우빈님과 소통하는 것도 그렇고..여러가지 미션의 내용도 단조롭지 않고 수준이 높은 것 같아 다시 한번 메타인지를 경험하게 해준다.강의의 경우, 시간상 처음부터 끝까지 다시 보지는 못하겠지만.. 불과 몇 개월 전 수강한 강의임에도 단어들이 어색하다... 😂어색했던 부분은 다시 강의를 보며 이해하고 보완하였다.읽기 좋은 코드를 작성하기 위해 학습한 내용을 기반으로 적절한 추상화를 적용하는 훈련을 하며 내 코드가 읽기 좋은 코드가 되도록 노력해야겠다. [출처]인프런 워밍업 클럽 : https://inf.run/Y4cf2강의 : https://inf.run/yTUP4 

인프런워밍업클럽백엔드3기발자국

10 2일 전
10

살짝 서두른 Spring 기반 테스트 코드 - 인프런 워밍업 클럽 3기 백엔드 코드✨

👣 발자국 책갈피인프런 워밍업 클럽 3기 백엔드 코드 발자국 1주차인프런 워밍업 클럽 3기 백엔드 코드 발자국 2주차인프런 워밍업 클럽 3기 백엔드 코드 발자국 3주차인프런 워밍업 클럽 3기 백엔드 코드 발자국 4주차✅미션PR 책갈피[미션 Day 2] 추상과 구체 예시 작성[미션 Day 4] 리팩토링 & SOLID 원칙[미션 Day 7] 리팩토링 연습[미션 Day 11] 단위테스트 작성[미션 Day 16] 레이어드 아키텍처 특징 및 테스트 작성법[미션 Day 18] Mock 어노테이션 종류 및 차이점 & BDD 패턴 매치🎼 알레그레토 : 조금 빠르게 3주 차부터는 미션과 발자국을 조금 서둘러 진행할 예정이다.앞서 언급했듯이, 이번 주부터 항해 플러스가 시작되어 최대한 진도를 빠르게 빼보려고 한다. 현재 날짜(3월 17일) 기준으로 Day 16 미션과 3주 차 발자국을 작성 중이며,이번 주 안에 Day 18 미션과 4주 차 발자국도 작성을 완료하는 것이 목표이다.(가능할지는 모르겠지만 ㅎㅎ) 조금 급하게 진행하는 감이 있어 개인적으로 많이 아쉽다.워밍업 클럽에만 온전히 집중할 수 있었다면 더 많은 성장을 할 수 있었을 텐데 말이다... 하지만, 후회는 하지 않는다. 다음 기수의 존재는 우빈님만 아시겠지만, 다음에 또 없을 수도 있는 기회를 놓치고 싶지 않았기 때문이다. 👍나는 위의 이미지처럼 Trello를 활용해 인프런 워밍업 클럽에 참여하고 있다.미션 제출 날짜가 일정하지 않다 보니, 제출 하루 전에 노티를 받도록 설정해 두고 유용하게 사용 중이다. 🙃💡 자기만의 언어로 키워드 정리하기 섹션 6. Spring & JPA 기반 테스트Layered Architecture레이어드 아키텍처의 단점 : 기술에 대한 강결합이 심하다는 단점이 존재Hexagonal Architecture도메인 모델은 외부의 것들을 아예 모른다.도메인 모델 중심 (멀티 모듈 및 시스템이 커진다면..)단위테스트 vs. 통합테스트단위테스트 만으로는 커버하기 어려운 영역이 존재 (여러 모듈 및 여러 객체가 협력하기 때문에)통합테스트란?여러 모듈이 협력하는 기능을 통합적으로 검증하는 테스트단위 테스트만으로는 기능 전체의 신뢰성을 보장할 수 없다.IoC, DI, AOPORM, 패러다임의 불일치, HibernateSpring Data JPAQueryDSL@SpringBootTest vs @DataJpaTest@DataJpaTest는 @SpringBootTest보다 가볍다.@DataJpaTest보다는 @SpringBootTest를 더 선호@DataJpaTest는 @Transactional이 있어 롤백이 된다.@SpringBootTest는 클렌징을 해주어야 한다.@SpringBootTest vs @WebMvcTest@SpringBootTest는 E2E 테스트, 즉 통합테스트 시 사용하는 어노테이션이다.@WebMvcTest는 Presentation Layer에 대한 단독 테스트시 사용하는 어노테이션이다.다른 레이어들은 mocking을 통해 동작을 제어한다.@Transactional(readOnly = true)테스트에서 사용 시, 롤백 되는 것에 유의 해야 한다.트랜잭션 경계 설정을 해야한다.엔드포인트를 잘 설계해야 한다.Optimistic Lock, Pessimistic Lock낙관적 락 : 데이터 충돌이 자주 발생하지 않을 것이라 낙관적으로 가정하고, 트랜잭션을 진행하는 방식데이터를 읽을 때는 락을 걸지 않고, 데이터를 업데이트 시 버전 비교하여 충돌 여부 판단성능 저하를 최소화, 동시성을 높이는 데 유리비관적 락 : 데이터 충돌이 자주 발생할 것이라 비관적으로 가정하고, 트랜잭션이 데이터를 사용할 때 미리 잠금을 거는 방식데이터 일관성을 유지하는 데 초점트랜잭션이 완료될 때까지 다른 트랜잭션이 데이터를 수정할 수 없음데드락 발생 가능CQRS명령 조회 책임 분리 : Command Query Responsibility Segregation읽기(조회)와 쓰기(명령)의 책임을 분리하는 소프트웨어 아키텍처 패턴장점성능 최적화확장성 증가데이터 모델 최적화비지니스 로직의 명확한 분리단점복잡성 증가데이터 동기화 문제트랜잭션 관리 어려움@RestControllerAdvice, @ExceptionHandler@RestControllerAdvice : ControllerAdvice의 기능을 하는데 JSON으로 응답을 해주는 Advice커스텀 예외를 던지고 @RestControllerAdvice의 @ExceptionHandler에서 예외를 처리할 수 있다.Spring bean validation@NotNull, @NotEmpty, @NotBlank도메인 요구사항에서 나오는 validation과 책임 분리해야한다.Controller 단에서는 최소한의 validation을 통한 검증이 이루어져야 한다.@WebMvcTestObjectMapperJackson 라이브러리에서 제공하는 클래스로, Java 객체와 JSON 간의 변환을 담당하는 역할직렬화, 역직렬화를 수행Mock, Mockito, @MockBeanMock : 실제 객체 없이 동작을 모방하여 단위 테스트를 수행하는 가짜 객체Mockito : Java에서 Mock 객체를 쉽게 생성하고 관리할 수 있는 라이브러리@MockBean : Spring 컨텍스트에 Mock 객체를 등록하여 실제 빈을 대체@Mock : 순수한 자바에서 Spring 컨텍스트가 필요하지 않을 때 사용@MockBean : Spring 컨텍스트에서 특정 빈을 Mocking 하고 싶을 때 사용👨🏻‍💻 미션 회고[미션 Day 11][미션 PR]#6스터디 카페 이용권 선택 시스템 단위 테스트 작성 미션 조건은 다음과 같다. ✔각 프로젝트 모두 강의 중에 작성한 tobe 패키지 코드를 기준으로 함 (lesson 6-4 가 가장 마지막 버전)✔3개 이상의 서로 다른 클래스 & 총 7개 이상의 테스트 작성 ➡ 단, 같은 인터페이스를 구현하고 있는 구현체들은 1개 클래스로 간주한다.✔무엇을 테스트하고자 했는지를 잘 나타낸 @DisplayName 작성하기✔BDD(given/when/then) 스타일 따르기 (주석으로 표기) 가장 작은 단위의 메서드 부터 단위 테스트를 작성하면서,테스트 커버리지를 높이기 위해, 모든 주요 로직에 대한 단위테스트를 작성하고자 했다. 단위테스트를 작성하면서 강의에서도 강조한 내용 중에 하나인 @DisplayName을 잘 작성하기 위해 많이 고민했다. '권'이라는 Pass의 의미를 '패스'로 통일하여 일관성있게 작성하였다.읽는 사람으로 하여금 뇌 메모리를 적게 쓰게 하기 위해 @DisplayName도 최대한 추상화해서 작성하려고 노력했다.테스트의 현상을 중점적으로 작성하지 않으려고 하였다. 하지만, 사용자 입력을 받는 클래스인 InputHandler의 단위테스트를 작성하는 과정에서 어려움이 있었다.InputHandler 내부에서 static으로 선언되어 있어 mocking도 하기 어려웠다.public class InputHandler { private static final Scanner SCANNER = new Scanner(System.in); }프로덕션 코드를 수정하지 않는다는 요구사항을 지키면서는 테스트가 어려웠다. 만약 프로덕션 코드를 수정한다면 테스트가 가능해질 것이다. 👆 Scanner를 외부세계로 분리하면 테스트가 가능할 것 같다.✌ InputHandler 상위의 인터페이스를 생성 후, 테스트 전용 support 성격의 구현체를 만들어서 테스트가 가능할 것 같다.  미션 제출 후, 차후에 프로덕션 코드를 수정해서 테스트를 작성 해볼 예정이다.🏃 돌아보며..Day 11 미션은 제출이 끝이 아니라, 중간점검 라이브에 이어 마지막 라이브에서 코드 리뷰 단계가 남아있다.중간 점검 때 세심한 코드 리뷰를 받고 수정하면서 많은 성장을 했기에 이번에도 신청하지 않을 이유가 없었다. 다른 러너 분들도 꼭 받아봤으면 한다. 😊아직까지 혼자 신청해서 뻘줌해서가 아니라... 진짜 좋은 기회이자 경험이다... 🤣코드 리뷰 대상자로 뽑히길 기대하며.. 마지막 4주차도 화이팅 💪[출처]인프런 워밍업 클럽 : https://inf.run/Y4cf2강의 : https://www.inflearn.com/course/readable-code-%EC%9D%BD%EA%B8%B0%EC%A2%8B%EC%9D%80%EC%BD%94%EB%93%9C-%EC%9E%91%EC%84%B1%EC%82%AC%EA%B3%A0%EB%B2%95/dashboard

백엔드인프런워밍업클럽백엔드3기발자국박우빈테스트코드실용적인테스트가이드

10 2일 전
이소 중입니다

[인프런 워밍업 클럽 스터디 3기] PM 워밍업클럽 4주차 발자국 & 회고

인프런 워밍업클럽 3기에 참여한 수강생의 강의/스터디 후기입니다.1. 강의PM이 무엇인지, PM이 중점을 두고 일해야 하는 것은 무엇인지 명확하게 알려준 강의. 강의에 나온 모든 내용이 선명했다. 심리학개론 같기도 하고 경영학원론 같기도 하지만, 결국 PM은 사업의 지속과 성장을 위해 누군가는 고민해야 하는 업무를 해야 하는 사람이라는 것을 여러 근거를 토대로 타당성 있게 말하고 있다.또한 직장인인 나에게 메타인지를 가질 수 있게 해주었다. 1주차 ‘PM이 하는 일’에서는 내가 회사에서 하고 있는 일이 얼마나 쉬운 난이도의 일인지 말과 글로 일깨워주었다. 팩트 폭격당한 거 맞다. 직장에서 역할의 모호성을 느끼는 기획자, PM이라면 꼭 들어보길 바란다.그 이유는 ‘기획’이라는 직무를 가진 사람이 궁극적으로 나아가야 할 방향성을 잡게 도와주기 때문이다. 우리가 현실적으로 현재 역할을 바꾸거나 업무를 버릴 수는 없다. 그러나 꾸준히 성장해야 하는 방향과 필요성이 무엇인지 알게 된다.2. 스터디인프런의 워밍업 클럽 스터디 일정은 수강생 모두를 고려한 일정으로 잘 짜여져 있다고 생각한다. 현재 다른 일과 병행하지 않고 강의만 집중해서 들을 수 있는 사람이라면 커리큘럼에 맞추어 하루 4시간 정도 심도 있게 공부한다면 빠른 시간 내에 많이 성장할 수 있을 것이다. 직장인의 경우, 소화 못 할 수준의 일정은 아니다. 그치만 강의에 나오는 개념에 대해 생각해 볼 내용들이 많기 때문에 중간중간 재생을 멈추고 곱씹어야 한다. 이렇게 놓고 보면 이론 흡수만 해도 1-2시간이 걸리기 때문에 강의만 들어도 잘 시간이다. 필자는 평소 11시-12시 사이에 취침하는 사람이지만 스터디 기간 동안 1-2시 사이에 잠들고 취미생활에 투자하는 시간도 조금씩 줄어들었었다. 인간적인 현상이다. 평일에 녹초가 되는 직장인 특성상 욕심을 내어 깊게 고민해볼 시간을 내기는 힘들다. 1-2시간 정도의 추가 자습을 하고 싶어도 현실적으로 어려웠기에 내용을 제대로 숙지하지 못했다고 길게 변명을 하는 것이다. 그래서 원래 일정상 4월에는 데이터 분석 강의를 들으려고 했으나 이번 PM/PO 강의에서 학습한 이론을 심화 학습하는 것으로 계획을 변경했다. 한 달간 배운 이론 내용들을 실무에 적용할 수 있는 사례를 적어보고 구체화해 볼 것이다. 사실 스터디와 관련되어 인프런에게 피드백 드리고 싶은 내용이 많은데 글로는 더 쓰긴 어려울 것 같다. 강의에서 주요하게 나왔던 고객을 아는 방법인 '심층 인터뷰' 기회가 있다면 참여할 의향이 있다.3. 마무리스터디 기간 동안 다른 분들은 어떤 식으로 공부하셨는지 궁금하던 차에 코치님께서 스터디원들을 만날 수 있는 기회를 마련해 주셨다. 직무 관련하여 외부 모임에 참여하는 것은 처음이기에 기대되고 설렌다. 우리는 제품이라는 이름 아래 엄청난 범위(영업/마케팅/개발/운영과 관련된 전반적인 지식)에 대해 공유하고 서로에게 많은 영감을 받을 것으로 있다. 참으로 뜻깊은 마무리가 될 것 같다. 한 달 동안 평일의 피곤함을 이겨낼 의지가 있는 누구나에게 추천해 주고 싶은 강의다.

Masocampus

[마소캠퍼스 GEN AI 인사이트] 텍스트만 쓰면 영상이 뚝딱! KLING AI 1.6의

이제는 ‘글’만 쓰면 영상이 만들어지는 시대예요! 🎬영상 생성 AI인 KLING AI 1.6은 텍스트 입력만으로도 현실감 넘치는 고화질 영상을 자동으로 만들어줘요.복잡한 촬영도, 긴 편집 시간도 필요 없는 이 혁신적인 도구! 창작자와 마케터에게 완전히 새로운 가능성을 열어주고 있어요✨KLING AI는 중국의 ‘콰이쇼우(Kuaishou)’에서 개발한 영상 생성 AI예요.사진 한 장이나 텍스트 프롬프트만으로도 영화처럼 생생한 영상 제작이 가능하답니다!최대 2분, 1080p 고화질까지 지원되며, ChatGPT 기반의 다른 모델보다도 더 긴 영상과 물리적 디테일을 제공해요.다양한 스타일의 콘텐츠 제작도 가능하니, 활용도가 정말 높겠죠? 🎥 KLING AI가 주목받는 이유, 궁금하시죠?단순히 영상을 만드는 걸 넘어 사람의 움직임, 상상의 장면까지도 리얼하게 구현해낼 수 있기 때문이에요!예를 들어, 말 위에 탄 사람의 자연스러운 동작, 커피잔 속 화산 폭발 같은 상상도 KLING은 실현해줘요🔥 이 모든 게 가능한 건 바로 3D Space-Time Attention 기술 덕분이에요.비슷한 AI 도구들과 비교해도 KLING은 확실히 뛰어나요!🔹 Sora는 표현력은 우수하지만 영상 길이는 1분 이하🔹 Pika/Runway는 스타일은 다양하지만 해상도가 다소 낮음반면 KLING은 2분 분량, 고해상도, 실제 같은 물리 효과까지 모두 갖춘 강력한 영상 AI랍니다👍KLING은 단순히 텍스트를 입력하는 것만으로 영상을 제작해요!입력된 프롬프트를 해석하고, 시각 요소를 설계한 뒤3D 공간 정보와 물리 기반 애니메이션을 구성해 고해상도 영상으로 완성하는 방식이죠.이 모든 과정을 AI가 자동으로 처리하니, 영상 제작이 정말 쉬워졌어요! 😎KLING 1.6 버전에서는 기능이 한층 더 업그레이드 되었어요!🚀 성능 195% 향상 → 더 빠르고 자연스러운 영상📺 1080p, 30fps, 2분까지 지원📱 다양한 화면 비율로 TikTok, 유튜브에도 최적화!영상 퀄리티가 눈에 띄게 좋아졌답니다!아무리 뛰어난 기술이라도 아직은 완벽하진 않겠죠?KLING은 현재 중국어 기반 서비스이고, 피부색이나 인종 표현의 정교함도 조금 부족해요.또 긴 대화나 복잡한 프롬프트에선 일부 어색한 장면도 나타날 수 있답니다.하지만 글로벌 출시와 함께 점차 개선될 예정이에요! 💪✔ 글만 쓰면 영상 완성!✔ 현실에서 일어나기 힘든 상상까지 실현!✔ 비용과 시간을 아끼는 창작 도구!✔ Sora보다 앞서가는 글로벌 트렌드 선도자! 영상 콘텐츠 시대, KLING을 주목하세요!AI의 가능성을 깨우는 마소캠퍼스와 함께, 더 스마트한 AI 활용법을 배워보세요! 😊마소캠퍼스와 함께 AI를 활용해 업무 혁신을 이뤄보세요!효율적이고 스마트한 일의 방식을 통해 성장할 수 있도록 도와드릴게요. 📌 관련 강의 <미드저니 마스터 클래스, 미드저니로 생성하고 포토샵으로 완성하는 AI 디자인 실무>AI 도구 미드저니와 포토샵을 활용해 실무에 바로 쓰이는 감각적인 디자인을 누구나 쉽게 완성!  

AI · ChatGPT 활용영상제작영상편집ai영상kling영상마케팅ai트렌드ai기술텍스트영상영상ai크리에이터

lkwo

인프런 워밍업 클럽 DAY 18 미션

1. @Mock, @MockBean, @Spy, @SpyBean, @InjectMocks 의 차이를 한번 정리해 봅시다.Mockito 어노테이션✔@Mock실제 객체가 아닌 가짜 객체를 생성하여 테스트에 사용합니다. 해당 객체는 아무런 동작을 하지 않으며, stub된 동작만 수행합니다.✔@Spy실제 객체를 감싸서 필요한 부분만 stub 처리할 수 있습니다.✔@InjectMocks테스트 대상 객체를 생성하고, 그 객체에 @Mock 또는 @Spy 객체를 자동으로 주입합니다.Spring Boot Test 어노테이션 ✔ @MockBean기존의 spring bean을 mock 객체로 대체합니다.✔ @SpyBean기존의 spring bean을 spy 객체로 대체합니다.2. 아래 3개의 테스트가 있습니다.내용을 살펴보고, 각 항목을 @BeforeEach, given절, when절에 배치한다면 어떻게 배치하고 싶으신가요?(@BeforeEach에 올라간 내용은 공통 항목으로 합칠 수 있습니다. ex. 1-1과 2-1을 하나로 합쳐서 @BeforeEach에 배치)@BeforeEach void setUp() { 1-1. 2-1. 3-1. 3-3. 사용자 생성에 필요한 내용 준비 1-3. 2-3. 3-5. 게시물 생성에 필요한 내용 준비 1-5. 2-5. 3-7 댓글 생성에 필요한 내용 준비 } @DisplayName("사용자가 댓글을 작성할 수 있다.") @Test void writeComment() { // given 1-2. 사용자 생성 1-4. 게시물 생성 // when 1-6. 댓글 생성 // then 검증 } @DisplayName("사용자가 댓글을 수정할 수 있다.") @Test void updateComment() { // given 2-2. 사용자 생성 2-4. 게시물 생성 2-6. 댓글 생성 // when 2-7. 댓글 수정 // then 검증 } @DisplayName("자신이 작성한 댓글이 아니면 수정할 수 없다.") @Test void cannotUpdateCommentWhenUserIsNotWriter() { // given 3-2. 사용자1 생성 3-4. 사용자2 생성 3-6. 사용자1의 게시물 생성 3-8. 사용자1의 댓글 생성 // when 3-9. 사용자2가 사용자1의 댓글 수정 시도 // then 검증 }공통되는 준비과정을 합쳐서 test fixture로 구성했습니다. (각 객체들의 생성 준비 과정)각 객체를 생성하는 코드도 겹치는 부분이 있으나, 각각의 단위 테스트를 이해하는데 있어서는 한 블럭 내에 있으면 좋겠다고 판단했습니다. 또한, 혹시 각 테스트에 객체를 구성할 때 필수 값들이 다를 수 있기때문에 set up에 위치하지 않았습니다.

바커스

서울에서 지금 바로 주문 가능한 쭈꾸미 배달맛집 TOP 7

서울 쭈꾸미 배달 맛집을 소개합니다. 4월 제철 해산물 쭈꾸미가 댕기면 아래 맛집을 보고집에서 편하게 배달해서 드시면서 가열찼던 하루를 마무리하고 행복한 시간 보내시길 바랍니다. 🍽 1. 아이도 OK! 마포 ‘팔팔쭈꾸미’ – 순한 마늘간장 맛집자극적이지 않은 마늘간장 쭈꾸미로 어린이도 먹기 좋은 메뉴입니다. 정식 세트는 밥, 볶음, 샐러드 구성으로 든든하면서도 부담 없는 가격입니다. 깔끔한 포장도 호평받고 있어요.🍽 2. 혼자 먹기 딱 좋은 ‘연남 미니쭈’ – 1인분 전용 덮밥혼밥족을 위한 1인 쭈꾸미덮밥 맛집으로 자취생과 직장인들에게 인기입니다. 반찬, 국 포함된 정갈한 구성이며, 가성비와 배달 속도 모두 우수하다는 평가가 많습니다.🌶 3. 매운맛 조절 가능한 ‘구로 매운쭈집’ – 불향 가득한 매운 쭈꾸미1~5단계 매운맛 선택이 가능해 취향에 따라 조절할 수 있습니다. 쌈채소, 주먹밥이 포함된 정식 구성이고, 불향과 감칠맛이 강해 매운맛 좋아하는 분들에게 추천돼요.🔥 4. 강남의 직장인 필수템 ‘쭈꾸미블루스’ – 점심 배달 강자강한 불맛이 특징인 쭈꾸미 정식 세트로 구성 퀄리티가 높습니다. 계란찜, 쌈야채 등 반찬도 다양해 제대로 된 한 끼를 배달로 즐길 수 있어요. 정성스러운 포장도 인기 요인입니다.🎁 5. 감성까지 챙긴 ‘고래쭈꾸미’ – SNS 핫플 포장 맛집트렌디한 비주얼과 포장으로 SNS에서 자주 언급되는 맛집입니다. 다양한 토핑 선택이 가능하며, 단짠단짠 양념이 중독적이에요. 밀키트도 함께 판매되어 활용도 높습니다.🌿 6. 건강하게 즐기는 ‘미쓰쭈꾸미’ – 여성 혼밥 인기 메뉴깔끔한 맛과 균형 잡힌 구성으로 여성 혼밥족에게 호평받는 곳입니다. 쭈꾸미 양도 넉넉하고 자극적이지 않아 가볍게 즐기기 좋습니다. 포장 감성도 만족도를 높여줘요.🔥 7. 불맛 끝판왕 ‘쭈쭈쭈쿡’ – 고기보다 맛있는 직화 쭈꾸미불향이 살아있는 직화 쭈꾸미로, 주먹밥과 환상 궁합을 자랑합니다. 기본 반찬도 정갈하고 양념이 진해서 재주문이 많습니다. 불맛 좋아하시는 분들에게 강력 추천!좀 더 상세한 정보가 필요하시면 아래 링크를 참조하시기 바랍니다.서울 쭈꾸미 배달 맛집

교양서울쭈꾸미맛집

당황한 수달

[인프런 워밍업 클럽 3기] PM/PO 3주 차 발자국

PM/PO에게 이제 지표는 필수적인 요소가 되었습니다. 이번 주 강의(시작하는 PM/PO들에게 알려주고 싶은, 프로덕트의 모든 것)에서도 프로덕트 지표 프레임워크와 데이터 축적 방법(event texanomy)을 중점적으로 다루었습니다.  3주 차 동안 무엇을 배웠는가?이번에 새롭게 알게 된 개념 중 하나는 Proxy Metric이었습니다. 목표를 직접적으로 측정하기 어려울 때, 대신 다른 지표를 활용해 목표 달성 여부를 파악하는 개념입니다.프로덕트 지표 프레임워크에서는 AARRR에 대해서는 익숙하게 알고 있었지만, 실제 현장에서는 CLV보다 Payback Period를 자주 활용한다는 점,고객이 서비스의 핵심 가치를 처음 깨닫는 Aha Moment를 만들기 위한 Setup Moment를 배웠습니다.또한 Engagement를 Breadth, Depth, Frequency, Efficiency 등다양한 측면을 함께 고려해야 한다는 점이 중요하게 다가왔습니다.마지막으로, Event Taxonomy를 설계할 때 역시 목적을 기반으로 Top-Down 접근을 하는 방법도 있지만실제 데이터를 바탕으로 Bottom-Up 접근을 하는 방법을 구분하는 것이 인상 깊었습니다.3주 차 회고2주 차에 계획했던 것처럼 이번 강의에서는 이미 알고 있던 지표 개념을 다시 한 번 복습하며, 잊고 있던 부분과 처음 접하는 내용들을 새롭게 배울 수 있었습니다.현재 제가 담당하는 서비스는 규모가 크고 오래 운영된 서비스이다 보니, WAU와 MAU 같은 기본 지표를 꾸준히 모니터링하고 있지만 큰 변동이 없는 상태입니다. 그래서인지 자연스럽게 지표 관리에 매너리즘이 생기기도 하는데요. 이번 기회를 통해 그런 모습을 돌아보고, 배운 개념들을 어떻게 현장에 적용할 수 있을지 고민하는 한 주가 되었으면 합니다.특히 실제로 지표가 안정적인 경우에는 어떤 방향으로 접근해야 할지 구체적으로 고민해봐야겠다는 생각이 듭니다. 다음 주 학습 계획은?제가 가장 관심 있어 하는 영역인 실험 설계와 가설 검증을 통한 프로덕트 성장과 관련된 강의로 구성되어 있는데요. 이번 주까지 배운 내용을 기반으로, 다음 주에는 조금 더 깊이 있게 집중하며 실질적인 성장 전략을 고민해보는 시간을 가져보고 싶습니다.

기획 · PM· PO김민우튜터인프런워밍업클럽스터디PMPO

채널톡 아이콘