블로그

감자

나는 AI에 대체될까?

AI 시대에는 분야를 막론하고 "AI가 내 자리를 대체하지 않을까?"라는 걱정을 피하기 어렵습니다.특히 개발자들은 코드 자동 생성 서비스가 폭발적으로 등장하면서 "이제 코딩 공부는 필요 없겠다"라고 생각할 수 있습니다.과연 이것이 사실일까요?우리는 정말 코딩 공부를 하지 않아도 될까요?이 질문에 답하기 전에 2025년 3월의 한 기사를 살펴보겠습니다. 빌게이츠가 예측한 AI 시대 유망한 직업 ‘세 가지’… "의사·교사는 AI에 대체될 것”다만 게이츠는 전 직종에서 AI가 인간을 대체할 것이라면서도 일부 직종의 경우 대체되지 않는 직업도 있을 것이라고 했다. 그는 "기계가 야구를 하는 것을 누구도 보고 싶지 않을 것"이라고 했다.그러면서 AI가 대체하기 힘든 직업으로 '코딩 개발자, 에너지 전문가, 생물학자' 등을 언급했다. 최신 AI 모델들이 등장할 때마다 개발자들이 가장 먼저 대체될 것이라는 주장이 나오고 있지만, 게이츠는 인간 전문가가 필수적이라고 보고 있다.게이츠는 AI가 코드를 생성할 수 있다는 것은 분명하지만, 이 과정에서 여전히 인간이 필요하다고 했다.인간 전문가는 오류를 식별하고 수정하며, 알고리즘을 개선하고 AI 개발을 강화하는 데 반드시 필요하다는 설명이다. 이어 게이츠는 생물학자도 AI가 대체하기 어려울 것이라고 주장했다. 빌게이츠가 언급한 AI 시대에도 대체되기 어려운 직업으로는 코딩 개발자, 에너지 전문가, 생물학자가 있습니다. 이 중에서 코딩 개발자에 초점을 맞춰 살펴보겠습니다.AI가 코드를 빠르게 생성할 수 있게 되면서 개발자의 생산성은 크게 향상되었습니다. 하지만 동시에 컴퓨터 과학에 대한 깊은 지식이 필요 없는 단순 코드 생성 작업은 AI가 신입 개발자의 역할을 대체하고 있습니다. 이러한 기술적 변화와 경제적 요인으로 신입 개발자 채용이 감소하고 있습니다.그렇다면 주니어 개발자들은 개발직을 포기하고 다른 진로를 모색해야 할까요? 앞서 본 기사에 따르면, 개발자가 여전히 필요한 이유는 AI가 생성한 잘못된 코드와 비효율적인 알고리즘을 수정해야 하기 때문입니다. 즉, AI가 만든 코드의 품질을 판단하고 개선할 수 있는 능력이 필수적이라는 뜻입니다.경험이 부족한 주니어 개발자들은 이러한 능력이 아직 미흡할 수 있습니다. 하지만 AI 시대에 살아남기 위해서는 반드시 이러한 역량을 키워야 합니다.  컴퓨터 공학(Computer Science)에 대한 지식은 이러한 능력을 키우는 데 필수적입니다.이는 주니어 개발자는 물론 시니어 개발자에게도 해당됩니다.컴퓨터 공학은 컴퓨터 구조, 운영체제, 자료구조와 알고리즘, 네트워크, 데이터베이스 등을 포함합니다.이번 포스트에서는 그 중 컴퓨터 구조에 초점을 맞추어 보겠습니다.컴퓨터는 0과 1을 스위칭 하는 장치로 만들어집니다.과거에는 릴레이라는 장치를 이용해 만들었고, 이보다 효율적인 진공관, 트랜지스터로 발전되어 왔습니다.현재 컴퓨터는 트랜지스터를 이용해 만들 수 있는데요.트랜지스터로 논리 회로를 구성하고, 논리 회로를 조합해 프로그래밍을 할 수 있는 컴퓨터를 만듭니다.이 과정에서 논리 연산과 논리 연산의 특성, 메모리의 구조와 특성, 기계어와 어셈블리어를 다루면서 메모리에 대한 추상적인 이해가 아니라 실제적인 이해를 할 수 있습니다.이를 이해한다면 C언어의 메모리 접근은 굉장히 쉽게 느껴질 수 있습니다.그리고 컴퓨터가 ‘진짜로 어떻게 동작하는지’를 이해할 수 있기 때문에 상위 계층에서 해결하기 어려운 최적화 작업도 할 수 있는 능력을 가지게 됩니다.단순히 코드를 빠르게 만들어 내는 것은 AI가 가장 잘하는 것입니다.AI가 주도하는 시대에 우리는 컴퓨터 공학에 대한 기초를 단단히 가지고 AI의 부족한 부분을 채워 확실한 경쟁력을 가졌으면 좋겠습니다.그렇지 않으면 대체될 것입니다. 컴퓨터 구조는 대학교 수업, 인터넷 강의, 서적 등 다양한 방법으로 배울 수 있습니다.제가 준비한 컴퓨터 구조 강의는 시각적 자료를 활용하여 개념을 쉽게 설명하고, 실제 8비트 컴퓨터를 직접 만들어보며 컴퓨터의 구조와 동작 원리를 깊이 이해할 수 있도록 구성했습니다. 컴퓨터 구조를 배우는 것은 단순히 AI 시대의 흐름을 쫓아가는 것이 아닌, 그 흐름을 주도할 수 있는 힘을 키우는 일입니다. 이 강의를 통해 여러분이 기술의 최전선에서 활약하며 세상의 변화를 이끌어낼 수 있도록 돕겠습니다.(2025년 5월 10일까지 30%할인 진행하고 있습니다)add_shortcode('course','336749','list')

컴퓨터 구조컴퓨터과학컴퓨터구조AI

제이든(양준식)

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

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

[인프런 비즈니스 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.

인프런비즈니스

이소 중입니다

[인프런 워밍업 클럽 스터디 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. 마무리스터디 기간 동안 다른 분들은 어떤 식으로 공부하셨는지 궁금하던 차에 코치님께서 스터디원들을 만날 수 있는 기회를 마련해 주셨다. 직무 관련하여 외부 모임에 참여하는 것은 처음이기에 기대되고 설렌다. 우리는 제품이라는 이름 아래 엄청난 범위(영업/마케팅/개발/운영과 관련된 전반적인 지식)에 대해 공유하고 서로에게 많은 영감을 받을 것으로 있다. 참으로 뜻깊은 마무리가 될 것 같다. 한 달 동안 평일의 피곤함을 이겨낼 의지가 있는 누구나에게 추천해 주고 싶은 강의다.

인프런인프런워밍업클럽스터디3기

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

강동훈

[인프런 워밍업 클럽 3기 - CS] - 1주차 발자국 (운영체제) - 1

💻 운영체제 들어가기📌 개요개인용 컴퓨터: Windows / MacOS대형 컴퓨터, 서버: Unix / Linux스마트폰, 테블릿: Android / IOS스마트워치, 냉장고, 세탁기: Embeded OS ❓컴퓨터는 운영체제가 있어야 동작하는가?✅ 없어도 동작은 가능하지만 처음 설계를 제외한 다른 기능 추가가 불가(유연성 X) - 전화기(통화만 가능) / 스마트폰(업데이트 및 설치) 운영체제가 하는 일1⃣ 프로세스 관리 (CPU)→ 여러 프로그램을 동시에 실행하고, CPU 자원을 효율적으로 배분2⃣ 메모리 관리 (메모리)→ 실행 중인 프로그램이 필요한 메모리를 할당하고 관리3⃣ 하드웨어 관리 (하드웨어)→ 키보드, 마우스, 프린터 등 장치를 제어하고 하드디스크 특정 영역의 데이터 저장관리4⃣ 파일 시스템 관리 (파일)→ 하드디스크의 효율적인 파일 저장과 관리 📌 역사🗓 1940년대- 미국 펜실베니아 대학교에서 미국 탄도 계산을 위해 30톤 무게의 전자 디지털 계산기 발명- 종이에 수기로 작성하여 테스트 진행 후, 수동으로 스위치와 배선을 연결하여 프로그래밍- 펀치 카드를 이용한 입출력은 속도가 느리며 진공관의 유지 보수 비용도 크다.- 하드웨어의 비용이 비싸기에, CPU의 효율적인 활용이 중요해진다. 🗓 1950년대- 아주 작은 크기의 직접회로 개발.- 컴퓨터가 삽입된 펀치카드를 직접 읽어 계산하고 프린터로 출력.- 기존 과정 (오버헤드 과다 발생)1. 프로그래머가 오퍼레이터에게 펀치카드 전달.2. 펀치카드를 컴퓨터에 삽입 후, 결과 도출.3. 오퍼레이터가 결과를 프로그래머에게 전달.- 싱글 스트림 배치 시스템 (Single-stream Batch Processing Systems)1. 오퍼레이터에게 여러개의 펀치카드를 전달.2. 컴퓨터는 다수의 펀치카드를 한 번에 하나씩 읽어가며 결과를 도출- 입출력 관리자(I/O Device Controller) 를 개발하여 입출력 중에도 CPU 계산 가능- 입출력이 끝나면 CPU에게 인터럽트 신호를 주고, CPU는 신호를 받아 처리- 출력은 CPU와 입출력 관리자 분리가 가능하지만, 입력은 작업이 완료되어야지만 처리가 가능하여 어쩔 수 없이 CPU의 대기가 필요해짐. 🗓 1960년대- 시분할 시스템(Time Sharing System)- CPU 시간을 여러 개의 프로그램에 분배하여 동시에 실행되는 것 같은 효과 제공- 하나의 컴퓨터에 다수의 터미널을 연결시켜, 여러 사용자의 작업이 가능해짐.- 벨 연구소에서 유닉스(UNIX) 운영체제 개발- 멀티 프로그래밍: 여러 개의 프로그램 동시 실행 가능- 다중 사용자: 여러 사용자가 한 대의 컴퓨터를 공유하여 사용- 트리구조 파일 시스템: 루트("/")를 기준으로 파일과 디렉터리가 계층형 구조로 정리- 보안 및 접근 권한: 파일, 디렉터리에 대한 권한('r', 'w', 'x') 설정 가능- 높은 이식성: 다양한 하드웨어에 쉽게 적용 가능- 쉡 기반 CLI 제공: GUI가 아닌 CLI를 통해 운영- 여러 프로그래밍 간 메모리 영역을 침범하는 문제 발생 🗓 1970 ~ 1980년대- 개인용 컴퓨터의 등장(애플 맥킨토시, MS DOS)- GUI(Graphic User Interface)의 등장 🗓 1990 년대- GUI 환경의 개인용 컴퓨터의 보급으로 다양한 응용 프로그램 등장- Excel, Word - Winow 운영체제의 대중화- UNIX를 기반으로 한 오픈소스 LINUX 운영체제의 등장➡ CPU의 효율성과 오버헤드의 감소를 위한 고민이 끊임없이 이루어짐. 📌 구조1. 사용자는 인터페이스를 통해 커널에 접근- GUI: 그래픽으로 커널과 상호작용으로 일반 사용자도 접근 가능- CLI: 텍스트 형태의 명령어로 커널과 상호작용 2. 어플리케이션을 시스템 콜을 통해 커널 접근어플리케이션이 직접 커널에 접근하여 하드 디스크에 파일을 저장할 경우, 중요한 데이터의 손실이 발생할 수 있다. 이를 방지하기 위해 시스템 콜을 이용하여 운영체제를 통해 하드디스크의 빈 공간에 데이터를 저장한다. 3. 커널이 하드웨어에 접근드라이버를 통해 커널은 하드웨어에 접근할 수 있다. 커널이 모든 하드웨어에 대한 드라이버를 저장할 수는 없으니, 하드웨어의 제조사에서 드라이버를 만들어 제공하고 커널이 이를 설치하면 접근 가능하다. 📌 컴퓨터 하드웨어와 구조기존에는 하드웨어로 프로그램을 만들었기에, 프로그램의 수정마다 배선와 스위치를 수동으로 변경해야 한다. ✅ 폰 노이만 구조 (Von Neumann Architecture)1. 프로그램 내장 방식- 프로그램과 데이터가 같은 메모리에 저장- 실행한 명령어를 CPU가 메모리에서 읽어와 수행2. 순차적 명령 실행- CPU는 한 번에 하나의 명령어를 순차적으로 실행3. 메모리, CPU, 입출력 장치로 구성- CPU(Central Pocessing Unit)1. 산술논리 연산장치: 실제로 데이터 연산을 담당2. 제어장치: 모든 장치들의 동작을 제어하고 지시3. 레지스터: CPU내에 계산을 위해 임시적으로 저장하는 공간- Memory1. RAM: 랜덤으로 데이터를 읽어도 속도는 일정, 휘발성2. ROM: 전력이 끊겨도 데이터 저장이 되지만 수정 불가 (BIOS)- 입출력 장치: 사용자와 컴퓨터 간 데이터 입/출력 관리 📌 컴퓨터 부팅 과정1. 컴퓨터의 전원을 누름2. ROM에 저장된 BIOS 실행1. CPU, RAM, 하드웨어에 이상 여부를 확인2. 이상이 없다면 하드디스크에 저장된 마스터 부트 레코드를 메모리로 올림3. 운영체제 동작3. 모니터에 운영체제의 동작을 실행 ✅ 마스터 부트 레코드(Master Boot Record, MBR)하드디스크, SSD, USB등 저장장치에서 부팅과 파티션 정보를 관리하는 0번 섹터1. 부트 로더(bootloader) 저장 - BIOS가 MBR을 읽고 부트 로더를 실행하여 운영체제로 부팅2. 파티션 정보 저장- 디스크의 파티션 구조를 관리 (최대 4개의 파티션)3. 디스크 식별- 디스크의 고유한 식별 정보를 포함 📌 인터럽트✅ 폴링(Polling)CPU가 지속적으로 장치나 하드웨어의 상태를 확인하여 필요한 작업을 처리하는 방식.- CPU의 비효율적인 자원 소모- 다수의 장치를 관리할 수록 CPU는 주기적으로 다수의 장치를 확인해야 하므로 다른 중요한 작업의 처리 속도가 느려질 수 있다.- 상태 확인 주기와 타이밍- 처리가 완료되어도 다음 폴링 주기까지 기다려야 하며 사용자 응답 또한 지연된다. ✅ 인터럽트(Interupt)프로세서가 현재 실행 중인 작업을 중단하고, 중요한 작업이나 이벤트를 즉시 처리- 하드웨어 인터럽트 - 시스템 외부의 하드웨어 장치에서 발생한 이벤트에 반응- 소프트웨어 인터럽트 - 소프트웨어, 프로그램에 의해 발생   

시스템 · 운영체제운영체제감자워밍업클럽3기

xx

[인프런 워밍업 클럽 3기 CS] 참여 후기

참여하게 된 계기CS 지식은 기술 면접에서도 자주 등장하고 실제 문제를 해결할 때도 중요한 기반이 되는데, 그에 비해 제 지식은 다소 얕다고 느꼈습니다.자료구조나 알고리즘은 어느 정도 익숙하지만 개념 위주로만 알고 있어 정리가 필요했고, 운영체제에 대한 이해는 특히 부족하다는 생각이 들었습니다. 이 기회에 자료구조와 알고리즘을 정리하고 운영체제의 기본을 다지고 싶어 참여하게 되었습니다. 좋았던 점운영체제는 내용이 추상적이고 딱딱하게 느껴질 수 있는데, 강의에서는 이를 예시나 그림으로 풀어 설명해줘서 이해하기가 훨씬 수월했습니다. 프로세스, 스레드, 메모리 구조 같은 어려운 개념도 시각적으로 설명되니 머릿속에 잘 그려졌고, 실무에서 어떻게 연결되는지도 자연스럽게 이해할 수 있었습니다.또한 자료구조와 알고리즘 파트는 깊이 있게 다루지는 않았지만, 주요 개념을 빠르게 훑는 데에는 도움이 되었고, 전체 흐름을 다시 한 번 정리할 수 있는 기회가 되었습니다. 아쉬운 점스터디가 끝나면 항상 아쉬운 점이 남는 것 같습니다. 개인적으로는 복습을 충분히 하지 못해서, 스터디에서 배운 내용을 완전히 소화하지 못한 것 같다는 점이 가장 아쉽게 느껴졌습니다. 조금 더 시간을 들여 복습하거나, 스터디 이후에도 학습을 이어나갈 계획입니다.

인프런인프런워밍업클럽스터디3기

xx 8일 전
suover

인프런 워밍업 클럽 스터디 3기 - 백엔드 클린 코드, 테스트 코드 후기

Readable Code: 읽기 좋은 코드를 작성하는 사고법Practical Testing: 실용적인 테스트 가이드강의와 함께한 인프런 워밍업 클럽 스터디 3기 - 백엔드 클린 코드, 테스트 코드 (Java, Spring Boot) 후기 입니다.소개이번 스터디 3기는 백엔드 클린 코드, 테스트 코드를 주제로 진행하였습니다. “Readable Code: 읽기 좋은 코드를 작성하는 사고법”과 “Practical Testing: 실용적인 테스트 가이드” 강의를 함께 학습함으로써, 더 나은 코드 가독성과 안정적인 테스트를 위한 다양한 방법론을 실습할 수 있었습니다.지난 2기 때 Kotlin과 Spring Boot로 백엔드 개발 전반을 경험하며 웹 애플리케이션 동작 원리를 학습했다면, 이번 3기에서는 코드 품질과 테스트라는 핵심 주제를 깊이 파고들어, 보다 깨끗하고 유지보수하기 좋은 코드, 그리고 신뢰도 높은 테스트 코드를 작성하는 법을 익혔습니다.아래에서는 각 주차별 학습 내용을 정리하고, 전체 스터디를 통해 느낀 점과 앞으로의 계획을 공유하고자 합니다. 1주차: 클린 코드를 위한 기초 - 추상과 구체, SOLID 원칙1주차 주요 학습 내용추상과 구체의 개념논리와 사고의 흐름SOLID 원칙 (SRP, OCP, LSP, ISP, DIP)리팩토링 미션 수행1주차에서는 코드 가독성을 높이는 가장 기본이 되는 개념들을 배웠습니다. 특히 추상과 구체의 균형을 어떻게 잡아야 코드가 명확해지고, 협업자가 빠르게 이해할 수 있는지 고민할 수 있었습니다.추상은 핵심 개념과 의도만 드러내도록 하는 장점이 있지만, 너무 과도하면 실제 구현과 괴리가 생겨 혼란을 줄 수 있다는 점을 배웠습니다.구체는 세세한 내용을 모두 표현해 직관적이지만, 과도해지면 복잡해지고 가독성이 떨어질 수 있으므로 주의가 필요했습니다.또한 Early Return이나 부정어 최소화 같은 리팩토링 기법을 적용해 보면서, 간단한 습관만으로도 코드의 가독성이 크게 높아진다는 점을 실감했습니다. 2주차: 코드 구조 및 리팩토링 - 패키지 구조, 주석, 적정 기술2주차 주요 학습 내용효과적인 주석 사용 방식변수와 메서드의 나열 순서패키지 구조 설계와 적정 기술(Over Engineering 지양)스터디카페 이용권 시스템 리팩토링2주차에서는 코드 구조 전반을 깔끔하게 정비하는 방법을 중심으로 학습했습니다.주석은 변동이 잦거나 오해를 일으킬 만한 내용을 담지 말고, 정말 필요할 때만 사용해야 한다는 것을 다시금 확인했습니다.변수와 메서드를 사용하는 순서대로 배치하고, 공개 메서드(public) → 비공개 메서드(private) 순서로 자연스럽게 구성함으로써, 읽는 흐름을 방해하지 않는 것이 중요하다는 점을 배웠습니다.특히 리팩토링 미션을 통해 패키지 구조가 지나치게 단순하거나, 반대로 너무 세분화되어 관리가 어렵지 않은지 돌아볼 수 있었습니다. 적정 기술을 사용하는 태도. 즉, 문제를 해결하는 데 필요한 만큼만 기술 스택과 디자인 패턴을 적용하는 것이 얼마나 중요한지도 체감하게 되었습니다. 3주차: Spring & JPA, 계층형 아키텍처, 통합 테스트3주차 주요 학습 내용Spring & JPA를 이용한 계층형 아키텍처 (Controller, Service, Repository)단위 테스트와 통합 테스트의 차이점 및 작성 방법MockMvc, @DataJpaTest 등을 활용한 테스트 실습Bean Validation과 ExceptionHandler로 유효성 검증 및 예외 처리3주차부터는 실무에서 흔히 쓰이는 Spring & JPA 기반의 계층형 아키텍처에 초점을 맞춰, 전체적인 흐름을 학습했습니다.Layered ArchitecturePresentation Layer(Controller)Business Layer(Service)Persistence Layer(Repository)이러한 구조를 바탕으로, MockMvc를 사용해 Controller부터 Service, Repository까지 이어지는 통합 테스트를 작성해 봄으로써, 애플리케이션 전반이 정상 동작하는지 확인할 수 있었습니다.또한 @NotNull, @Positive 등 Bean Validation 애너테이션과 @RestControllerAdvice를 통한 예외 처리 방식도 연습해보았습니다. 4주차: Mock, Spy, @MockBean / @SpyBean, 레이어별 테스트 전략4주차 주요 학습 내용Mockito에서의 @Mock, @Spy, @InjectMocks스프링 환경에서의 @MockBean, @SpyBean레이어별 테스트 설계: 단위 테스트 vs 통합 테스트테스트 시나리오 설계(given-when-then)와 @BeforeEach 활용마지막 주차에서는 테스트 유지보수와 테스트 코드 설계의 중요성을 배웠습니다.@Mock, @Spy를 통해 순수 단위 테스트 환경에서 외부 의존성을 제거(또는 부분 제거)하여 빠른 피드백을 받을 수 있었습니다.반면 @MockBean, @SpyBean은 스프링 애플리케이션 컨텍스트를 띄운 상태에서 기존 Bean을 모의 객체로 교체할 수 있어, 좀 더 넓은 범위의 통합 테스트에 활용할 수 있었습니다.레이어별 테스트 (Controller, Service, Repository)를 분리해 진행하면 테스트가 더욱 명확해지고, 문제 발생 시 어느 레이어가 원인인지 빠르게 파악할 수 있음을 깨달았습니다.가장 크게 느낀 점은 ‘테스트도 코드의 일부’라는 사실이었습니다. 테스트 코드를 지속적으로 관리하고 개선해야, 유지보수가 필요한 시점에 믿을 수 있는 안전망이 되어준다는 것을 명확히 알게 되었습니다.회고이번 스터디 3기를 통해 "클린 코드와 테스트"라는 주제의 중요성을 다시 한번 실감했습니다. 단순히 기능만 구현하는 것이 아닌, 가독성, 유지보수성, 테스트 커버리지까지 고려해야 진정한 의미의 소프트웨어 품질을 높일 수 있음을 배웠습니다.아쉬웠던 점 & 보완 계획실프로젝트 적용: 강의와 미션 중심의 학습이기에, 실제 현업 수준의 복잡도를 가진 코드에 적용할 기회가 많지 않았습니다. 앞으로 개인/팀 프로젝트에서 적극 활용해 경험치를 높이고자 합니다.테스트 시나리오 다양성: 해피 케이스 중심의 테스트가 많았기에, 예외 상황과 경계값 테스트를 더 폭넓게 다루면 좋았을 것 같습니다. 마무리인프런 워밍업 클럽 스터디 3기를 통해, 클린 코드와 테스트 코드 작성 역량을 크게 향상시킬 수 있었습니다.코드: 명료한 이름 짓기, 중복 제거, 적절한 추상화 등을 통해 읽기 좋고 변화에 강한 코드를 추구하게 되었습니다.테스트: 프로젝트가 확장되더라도, 안정성을 확보할 수 있는 테스트 설계가 얼마나 중요한지 다시금 느끼게 되었습니다. 이번 스터디에서 성실히 참여한 결과, 2기에 이어 3기에서도 우수러너로 선정되는 영광도 누릴 수 있었습니다. 앞으로도 스스로 레거시 코드를 리팩토링하거나, 프로젝트를 확장하면서 배운 내용을 꾸준히 적용할 것입니다. 스터디에서 함께한 분들, 그리고 인프런과 워밍업 클럽 관계자분들께 감사드리며, 지속적인 학습과 공유를 통해 더 나은 개발자가 되도록 노력하겠습니다. 감사합니다!

백엔드인프런인프런워밍업클럽스터디3기워밍업백엔드후기클린테스트코드

leeebug

워밍업 클럽 스터디 3기 FS - 스터디 후기

워밍업 클럽을 처음 알게 된 건 사실 이번 3기가 아니라 지난 2기 때였다.당시에도 이 스터디에 흥미가 있었지만 개인적인 사유로 아쉽게도 참여하지 못했는데, 이번 3기에 Next.js 과정이 포함되었다는 소식을 듣고 주저 없이 참여 신청했다.결과적으로는 아주 만족스러운 선택이었다. SLL 회고Startany를 배제하고, 모든 컴포넌트에 명시적 타입 선언을 적용해보았다.단일 책임 원칙을 기반으로, UI와 비즈니스 로직을 분리하는 컴포넌트 구조화를 시도해보았다.클라이언트와 서버 상태를 별도로 관리하기 위해서 React Query를 도입해보았다.Zod + React Hook Form 조합을 처음 적용해보면서 스키마 기반 유효성 검증의 흐름을 경험해보았다.LearnUI와 비즈니스 로직을 분리하는 패턴은 가독성 향상에는 도움이 되었지만, 반대로 전체적인 복잡도가 증가할 수 있다는 점도 느꼈다.Next.js 환경에서는 SWR보다 React Query가 더 세밀한 제어와 상태 관리에 적합하다는 것을 체감했다.기능을 직접 구현하는것도 충분히 의미가 있지만, 이미 검증된 라이브러리를 적절히 도입하는 것이 생산성 측면이나 유지보수 측면에서 더 큰 도움이 될 수 있다는 점을 배웠다.Love타입 설계와 컴포넌트 구조를 직접 개선해본 경험 자체만으로 코드에 대한 자신감과 설계 감각이 향상되었음을 체감했다.주차별 다른 주제의 과제를 만들어가는 과정을 통해서 작은 성공의 중요성을 다시 한 번 느낄 수 있었다.  마무리짧다면 짧은 4주의 기간동안 매주 스스로 도전하고 개선해가는 과정을 통해 한 걸음 더 성장할 수 있었다고 느꼈다.혹시나 아직도 워밍업 클럽 참여를 망설이고 있다면 꼭 한 번 경험해보길 추천하며 스터디 회고를 마친다.

풀스택인프런인프런워밍업클럽스터디3기

Masocampus

[마소캠퍼스 GEN AI 인사이트] 트럼프 관세폭탄, 한국 산업은 얼마나 흔들렸을까?

이제 관세 하나가 나라 경제를 뒤흔드는 시대예요! 🌍트럼프 정부의 ‘관세폭탄’은 단순한 무역 조치가 아닌, 산업 전반에 영향을 주는 강력한 변수였어요.특히 한국은 자동차, 반도체, 철강 등 주력 수출 산업이 직접적인 타격을 받으며 큰 혼란을 겪었죠. 👉 더 자세한 분석과 수치는 아래 링크에서 보고서를 무료로 다운로드받을 수 있어요!📥 트럼프 관세 정책과 한국 산업 영향 보고서 보기 1. 40p 워드로 정리된 문서 보고서2. 30p PPT 정리된 프레젠테이션 보고서 단기적인 기회도 있었지만, 장기적으로는 더 큰 리스크가 숨어 있었던 이번 사례!글로벌 정책 변화가 우리 산업에 어떤 파장을 일으켰는지, 함께 들여다볼까요?✨트럼프 전 대통령은 ‘미국 제조업 살리기’를 목표로 해외 제품에 높은 관세를 매기기 시작했어요.그 핵심 키워드는 ‘America First’였죠.이 정책의 배경에는 무역적자 해소, 제조업 부활, 그리고 '공정무역'이라는 명분이 있었답니다.하지만 그 여파는 글로벌 경제 전체로 퍼져나가게 되었어요.트럼프 정부는 두 가지 방식으로 관세를 부과했어요.상호관세: 상대국이 미국에 부과하는 만큼 미국도 같은 비율로 부과!→ 한국엔 무려 25%가 부과되어 일본(24%), EU(20%)보다 높았어요.보편관세: 모든 나라에 공통적으로 적용되는 기본 관세로 철강, 자동차, 반도체 등이 대상이었죠. 이로 인해 한국 수출 기업들은 치열한 경쟁 환경에 놓이게 되었어요.한국 자동차 산업은 큰 타격을 받았어요.미국으로 수출되는 차량과 부품에 25%의 관세가 부과되었고,그 결과 한국 자동차 브랜드의 시장 점유율은 3.2%에서 1.8%까지 하락할 것으로 예상되었어요.차량 가격이 오르며 소비자 구매력도 줄어들었죠. 현대차와 기아차는 이런 위기를 극복하기 위해 미국 내 생산 공장을 더욱 확대했어요.반도체 산업은 조금 복잡한 상황이에요.중국산 제품을 대체하려는 미국의 수요가 늘어나며 단기적으로는 기회였지만,공급망 재편과 생산비 증가, TSMC와의 경쟁 심화 등으로 장기적인 불확실성이 커졌어요. 삼성과 SK하이닉스는 미국 반도체 정책에 발맞춰 수혜를 입었지만, 장기적으로는 고민이 필요하겠죠.철강산업 역시 관세 25% 부과의 영향을 피하지 못했어요.대미 수출량은 10.6% 감소했고, 생산비 압박은 자동차 및 가전제품 가격 인상으로 이어졌죠. 이에 따라 포스코, 현대제철은 미국 내 생산 설비 신설까지 고려하게 되었어요.한눈에 보면, 한국 산업은 트럼프의 관세폭탄 앞에 크게 흔들렸습니다.수출이 많다고 해서 안심할 수 없고, 단기적 기회도 장기적으론 리스크가 될 수 있다는 점을 보여주었죠. 이번 사례를 통해 국제정세에 대한 이해는 물론, 기업의 전략적 대응이 얼마나 중요한지도 알 수 있어요.앞으로도 글로벌 정책 변화에 주목하면서, 유연하게 대응하는 것이 필요하겠죠 :)마소캠퍼스와 함께 AI를 활용해 업무 혁신을 이뤄보세요!효율적이고 스마트한 일의 방식을 통해 성장할 수 있도록 도와드릴게요. 📌 관련 강의 <회사가 원하는 효율적인 AI 활용 – 회의 준비부터 기록까지>회의 준비부터 회의록 작성까지, AI가 다 해드립니다! 반복 업무는 자동화하고, 중요한 일에 집중하세요.  

경영 · 전략관세수출규제미국우선주의트럼프관세경영경제이슈ai트렌드마소캠퍼스한국경제무역전쟁

워밍업 클럽 3기_PM/PO_후기

PM이란 무엇일까 막연한 의문으로 강의를 신청했다. 신청 당시 내가 가진 직무 전문성에 대해 회의감이 드는 시기였다. 손에 잡히는 성과물 하나 없는 2년간의 PM 업무 경험을 가지고 앞으로 어떻게 커리어를 쌓아야 하나 막막했다.완강 후에 다시 PM이란 무엇인지 생각해 보았을 때, 이전보다는 좀 더 넓게 PM의 직무를 바라볼 수 있게 되었다. 강의 내용 중 제품과 사용자에 대한 전문가로 PM의 정의하는 부분이 인상 깊었고, 사용자와 제품에 대한 전문성을 기르는 방법과 따라오는 다양한 개념들도 유익했다.결국, PM이란 직무를 돌이켜보면 딱 떨어진다기보다는 각 회사마다 필요한 상황에 따라 다양한 역할을 수행하는 직무였다. 더 이상 PM 구직 공고를 보며 내가 가진 전문성이 떨어진다고 한탄하지 않는다. 오히려 사용자와 제품 전문가로 성장하기 위해 나는 어떤 노력을 해왔고 앞으로는 무엇을 더 해보고 싶은지 생각해 보게 되었다.강의를 추천하자면 PM에 대해서 막연한 호기심을 가진 입문자나 2-3년 경력을 가진 주니어에게 직무에 대한 시야를 넓혀주는 좋은 강의라고 생각한다. 워밍업 클럽으로 강의를 수강하며 매일 동기부여를 받을 수 있는 것도 좋았다. 커리어 성장에 대해 막연한 불안감이 들 때 한 번쯤 도전해 보길 추천한다.

기획 · PM· PO인프런인프런워밍업클럽스터디3기

wonderson

[워밍업 클럽 3기 CS 전공지식] 자료구조와 알고리즘 특별 미션

문제실수로 워밍업 클럽 출석을 빼먹었는데 우연히 데이터를 수정할 수 있는 권한이 주어졌습니다. 러너분의 이름(name)과 출석수(count)가 저장된 배열에서 여러분(나)의 데이터를 퀵정렬을 이용해 오름차순 정렬하고 가장 첫 번째 데이터인 여러분의 출석수를 변경하도록 코드를 작성해주세요. (퀵정렬 구현 부분도 변경)// 퀵소트 구현 부분...(생략) let user1 = { name: "홍길동", count: 5 }; let user2 = { name: "임꺽정", count: 4 } let user3 = { name: "이순신", count: 3 } let user4 = { name: "나", count: 1 } let user5 = { name: "짱구", count: 5 } let arr = [user1, user2, user3, user4, user5] console.log("===== 정렬 전 ====="); console.log(arr); quickSort(arr, 0, arr.length - 1); console.log("===== 정렬 후 ====="); console.log(arr);예상 결과===== 정렬 전 ===== [ { name: '홍길동', count: 5 }, { name: '임꺽정', count: 4 }, { name: '이순신', count: 3 }, { name: '나', count: 1 }, { name: '짱구', count: 5 } ] ===== 정렬 후 ===== [ { name: '나', count: 5 }, { name: '이순신', count: 3 }, { name: '임꺽정', count: 4 }, { name: '홍길동', count: 5 }, { name: '짱구', count: 5 } ] 문제 해결[깃허브 코드 - Java로 작성]이전에 작성한 퀵소트 정렬 코드 참고하기다른 점숫자 배열이 아닌 객체 배열을 만들어야 한다.객체 배열 안에서 count 로 오름차순을 한다.첫 번째 해결 - count로 오름차순하기name과 count를 함께 저장할 수 있는 Runner 클래스 생성이전 코드에서 int[] 배열 대신 Runner[] 배열로 변경package sectionThree.quickSort.specialMission; public class Runner { private String name; private int count; public Runner(String name, int count) { this.name = name; this.count = count; } public int getCount() { return count; } @Override public String toString() { return "{name: '" + name + "', count: " + count + '}'; } }package sectionThree.quickSort.specialMission; import java.util.Arrays; public class QuickSortMission { public static void quickSortM(Runner[] runners, int left, int right) { if (left <= right) { int pivot = divideM(runners, left, right); quickSortM(runners, left, pivot - 1); quickSortM(runners, pivot + 1, right); } } public static int divideM(Runner[] runners, int left, int right) { int pivot = runners[left].getCount(); int leftStartIndex = left + 1; int rightStartIndex = right; while (leftStartIndex <= rightStartIndex) { while (leftStartIndex <= right && pivot >= runners[leftStartIndex].getCount()) { leftStartIndex++; } while (rightStartIndex >= (left + 1) && pivot <= runners[rightStartIndex].getCount()) { rightStartIndex--; } if (leftStartIndex <= rightStartIndex) { swapM(runners, leftStartIndex, rightStartIndex); } } swapM(runners, left, rightStartIndex); return rightStartIndex; } public static void swapM(Runner[] runners, int index1, int index2) { Runner temp = runners[index1]; runners[index1] = runners[index2]; runners[index2] = temp; } public static void main(String[] args) { Runner user1 = new Runner("홍길동", 5); Runner user2 = new Runner("임꺽정", 4); Runner user3 = new Runner("이순신", 3); Runner user4 = new Runner("나", 1); Runner user5 = new Runner("짱구", 5); Runner[] runners = {user1, user2, user3, user4, user5}; System.out.println("===== 정렬 전 ====="); System.out.println(Arrays.toString(runners)); quickSortM(runners, 0, runners.length - 1); System.out.println("===== 정렬 후 ====="); System.out.println(Arrays.toString(runners)); } }결과===== 정렬 전 ===== [{name: '홍길동', count: 5}, {name: '임꺽정', count: 4}, {name: '이순신', count: 3}, {name: '나', count: 1}, {name: '짱구', count: 5}] ===== 정렬 후 ===== [{name: '나', count: 1}, {name: '이순신', count: 3}, {name: '임꺽정', count: 4}, {name: '홍길동', count: 5}, {name: '짱구', count: 5}] 두 번째 해결 - 가장 첫 번째 데이터인 여러분의 출석수를 변경3번 코드에서 추가한 부분quickSortM()을 호출하고 모든 재귀 함수 호출이 끝난 시점인 if 가 필요Runner 클래스에서 setCount() 을 호출해서 변경최소/최대 참여수는 불변값으로 변수 선언문제점이 있다. 5번에서 해결하기package sectionThree.quickSort.specialMission; public class Runner { private String name; private int count; private static final int MIN_CHECK = 0; // 최소 참여수 private static final int MAX_CHECK = 5; // 최대 참여수 public Runner(String name, int count) { this.name = name; this.count = count; } public int getCount() { return count; } public void setCount(int count) { if (!(MIN_CHECK <= count && count <= MAX_CHECK)) return; this.count = count; } @Override public String toString() { return "{name: '" + name + "', count: " + count + '}'; } }package sectionThree.quickSort.specialMission; import java.util.Arrays; public class QuickSortMission { public static void quickSortM(Runner[] runners, int left, int right) { if (left <= right) { int pivot = divideM(runners, left, right); quickSortM(runners, left, pivot - 1); quickSortM(runners, pivot + 1, right); } // 정렬이 다 끝난 후 - 가장 첫 번째 데이터인 '나'의 출석수를 변경 if (right == runners.length - 1) { runners[0].setCount(5); } } public static int divideM(Runner[] runners, int left, int right) { int pivot = runners[left].getCount(); int leftStartIndex = left + 1; int rightStartIndex = right; while (leftStartIndex <= rightStartIndex) { while (leftStartIndex <= right && pivot >= runners[leftStartIndex].getCount()) { leftStartIndex++; } while (rightStartIndex >= (left + 1) && pivot <= runners[rightStartIndex].getCount()) { rightStartIndex--; } if (leftStartIndex <= rightStartIndex) { swapM(runners, leftStartIndex, rightStartIndex); } } swapM(runners, left, rightStartIndex); return rightStartIndex; } public static void swapM(Runner[] runners, int index1, int index2) { Runner temp = runners[index1]; runners[index1] = runners[index2]; runners[index2] = temp; } public static void main(String[] args) { Runner user1 = new Runner("홍길동", 5); Runner user2 = new Runner("임꺽정", 4); Runner user3 = new Runner("이순신", 3); Runner user4 = new Runner("나", 1); Runner user5 = new Runner("짱구", 5); Runner[] runners = {user1, user2, user3, user4, user5}; System.out.println("===== 정렬 전 ====="); System.out.println(Arrays.toString(runners)); quickSortM(runners, 0, runners.length - 1); System.out.println("===== 정렬 후 ====="); System.out.println(Arrays.toString(runners)); } } 결과===== 정렬 전 ===== [{name: '홍길동', count: 5}, {name: '임꺽정', count: 4}, {name: '이순신', count: 3}, {name: '나', count: 1}, {name: '짱구', count: 5}] ===== 정렬 후 ===== [{name: '나', count: 5}, {name: '이순신', count: 3}, {name: '임꺽정', count: 4}, {name: '홍길동', count: 5}, {name: '짱구', count: 5}] 두 번째 해결 이어서 - 가장 첫 번째 데이터인 여러분의 출석수를 변경현재 코드결과는 예상 결과처럼 잘 나온다.그런데 피벗을 기준으로 정렬할 때마다 Runner[] 첫 번째 배열의 count가 5로 변경된다.정렬 도중에 여러 번 첫 번째 요소의 값을 변경할 가능성이 있어서 의도한 대로 동작하지 않을 수 있다.내가 원했던 로직모든 정렬이 끝난 후 Runner[] 첫 번째 배열의 count가 5로 변경된다.해결 방법quickSortM() 함수 종료 후 main() 함수에서 runners[0].setCount(5); 진행 - 강사님 픽quickSortM() 에 depth 변수를 추가해서 정렬 후 0 depth인 경우에 runners[0].setCount(5); 를 진행해결 방법 2번으로 진행해보자package sectionThree.quickSort.specialMission; import java.util.Arrays; public class QuickSortMission { public static void quickSortM(Runner[] runners, int left, int right, int depth) { if (left <= right) { int pivot = divideM(runners, left, right); quickSortM(runners, left, pivot - 1, depth + 1); quickSortM(runners, pivot + 1, right, depth + 1); } if (depth == 0) { // 정렬이 다 끝난 후 - 가장 첫 번째 데이터인 '나'의 출석수를 변경 runners[0].setCount(5); } } public static int divideM(Runner[] runners, int left, int right) { int pivot = runners[left].getCount(); int leftStartIndex = left + 1; int rightStartIndex = right; while (leftStartIndex <= rightStartIndex) { while (leftStartIndex <= right && pivot >= runners[leftStartIndex].getCount()) { leftStartIndex++; } while (rightStartIndex >= (left + 1) && pivot <= runners[rightStartIndex].getCount()) { rightStartIndex--; } if (leftStartIndex <= rightStartIndex) { swapM(runners, leftStartIndex, rightStartIndex); } } swapM(runners, left, rightStartIndex); return rightStartIndex; } public static void swapM(Runner[] runners, int index1, int index2) { Runner temp = runners[index1]; runners[index1] = runners[index2]; runners[index2] = temp; } public static void main(String[] args) { Runner user1 = new Runner("홍길동", 5); Runner user2 = new Runner("임꺽정", 4); Runner user3 = new Runner("이순신", 3); Runner user4 = new Runner("나", 1); Runner user5 = new Runner("짱구", 5); Runner[] runners = {user1, user2, user3, user4, user5}; System.out.println("===== 정렬 전 ====="); System.out.println(Arrays.toString(runners)); quickSortM(runners, 0, runners.length - 1, 0); System.out.println("===== 정렬 후 ====="); System.out.println(Arrays.toString(runners)); } } 결과===== 정렬 전 ===== [{name: '홍길동', count: 5}, {name: '임꺽정', count: 4}, {name: '이순신', count: 3}, {name: '나', count: 1}, {name: '짱구', count: 5}] ===== 정렬 후 ===== [{name: '나', count: 5}, {name: '이순신', count: 3}, {name: '임꺽정', count: 4}, {name: '홍길동', count: 5}, {name: '짱구', count: 5}]  

바커스

챗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 19일 전
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 19일 전
채널톡 아이콘