블로그

인프런 옥돌

조직구조와 동기를 이끌어 내는 일.

https://www.youtube.com/watch?v=HtC9APOIvkw   영상 내용이 좋아서 요약해보았습니다. :-)   경영에 있어 제일 중요한 건 조직을 구성하는 일이다. (화자는 그렇게 생각한다.) 조직 관련 용어중에 사일로 현상이란 단어가 있다. 사일로란 곡물이나 가축의 사료를 저장하는데 저장되는 시설이다. 곡물이 서로 섞이지 않도록 칸을 구분지어 놓은 것인데, 조직 구성을 이렇게 해놓고 왜 같이 일하지 않아요? 라고 물으면 안된다. 조직 형태를 그렇게 만들어 놓았기 때문이다.         섞진 않더라도 횡으로 묶일 수 있도록 만들어 주는 것. 그래서 종으로 횡으로 엮이고 서로 커뮤니케이션 하며 일하는 방식이 요즘의 애자일 방식이다. 애자일 조직에선 보스가 2명이다. 종과 횡으로 상사가 있다. 실리콘밸리에선 익숙하고 발달된 형태의 조직 구조이다. 이런 조직에선 커뮤니케이션이 발달할 수 밖에 없다. 줌 같은 커뮤니케이션 툴도 이런 배경에서 나왔다.    <조직을 구성할때 고려해야 할 사항>  1. 전략을 토대로 형성되어야 한다.  2. 정보의 프로세스를 규정해야 한다. 3. 명확한 보상 체계가 있어야 한다. 4. 용병술   이 네가지를 디테일하게 세워넣고 조직을 구성해야 사람들이 움직이고 조직이 돌아간다. 다만 구글 / 카카오가 이렇게 일한다고 해서 기존 한국 기업들의 수직적인 조직구조가 좋지 않다고 생각하지 않는다. 그 방식으로 일해온 조직들도 충분히 대단한 성과를 내어왔다. 그걸 다 바꿔야 한다고 말하고 싶지 않다.    "구성원에게 얼마만틈 동기와 열망을 끄집어 낼 것인가?"는 우리의 일이 세상을 어떻게 더 낫게 만드는가? 비전과 미션, 가치와 목표 같은 것들이라고 생각한다. 더불어 지위나 금전적인 보상이 가까이 눈에 보일때 동기와 열망이 움질일 것이다.    팀원의 몰입과 행복을 끄집어 내는 것이 중요하다. 우리 팀원들은 언제 몰입하고 열심히 일하지? 그들은 누구와 밀접하게 일하지? 무엇 때문에 떠나고, 남아있는가? 를 생각해야 한다. 결국 팀원은 본인의 매니저, 본인이 속한 조직, 동료와 일한다. 그가 속한 조직장, 동료와의 관계가 중요하다. 실리콘밸리의 큰회사들은 피플 애널리틱스라는 걸 도입해 실험을 지속한다. 매일 바이오리듬을 체크해 어느 시간대에 가장 능률이 오르는지 분석하기도 한다. 오후 1시~3시 사이에 집중력이 낮기에 해당 시간대에 회의나 미팅을 잡도록 권장하는 식이다.    구체적인 것들을 바꾸라고 조언하고 싶다. 1. 실질적으로 일하는 방법. 2. 피드백 하는 문화, 그리고 과거의 일을 이야기하기보다 다음 6개월에 무엇을 할 건지 이야기하면 좋겠다. 또한 평가의 대상이 개인보다 조직이어야 한다고 생각한다.    스타트업은 스포츠 팀과 같다. 우리는 이겨야 하는 싸움을 하고 있고, 개인이 아니라 팀으로 이겨야 한다. "winnig as a team" 개인의 행동 변화를 이끌어내려면 피드백이 중요하다. 좋은 피드백은 맥락을 짚고, 그때그때 바로 이루어지는 피드백이다. 2월에 일어난 일을 연말에 평가한다면 맥락을 다까먹고 결과만 남는다. 피드백은 바로, 적시에 하는것이 좋다. 인사팀이 시켜서 하는 평가가 아니라 팀원이 서로 피드백을 주고 받고 회고하는 걸 매일하는 습관으로 몸에 배도록 해야 한다.    피드백할때 감정을 표출하거나 '너 이 자식~!@#$!@#%', 인생을 언급하거나 가족, 인간성을 언급하지 말라. 당시의 상황, 행동만을 언급하라. 상황과 행동에 따른 결과와 미래에 어떻게 개선할지를 이야기하면 된다. SBIT를 생각하며 피드백하자. (Situation | Behavior | Impact | Tommorow) 시간이 많이 지난뒤 결과만을 두고 질책하면 근본적으로 나아지기 어렵다. 당시 상황과 액션, 그에 따른 결과와 미래의 개선 지점을 이야기하자. 그것만 하면된다.     "winnig as a team"             

보안 · 네트워크 기타조직구조피드백회고

셰리

IT 업계 회고 문화 겉핥기 🍉

'뒤를 돌아본다'는 의미의 회고. 1~4주 정도의 짧은 주기(스프린트) 안에 결과물을 구현, 배포한 뒤 빠르게 제품을 개선하는 루틴을 반복하는 애자일(Agile) 프로세스와 관련이 깊다고 해요.오늘은 직장인들에게, 특히 IT 업계 종사자에겐 일상과도 같은 회고 문화를 빠르게 훑어보려고 합니다. 회고에 대한 인프런 팀원의 생각도 담겨 있으니 끝까지 읽어보세요! 😆회고는 왜 해야 할까?회고는 지나온 프로젝트 과정에서의 문제를 찾고 발전하기 위해 진행한다고 해요. 반복적인 문제 상황을 줄이고 효율적인 업무 방식을 찾기 위한 과정인 것이죠.비케이(Product Designer) ⚒️회고의 목적은 'Better'라고 생각해요. 문제 상황을 공유하면서 피드백이나 개선점을 찾고, 잘한 부분은 칭찬하고. 그러면서 프로젝트를 통해 무엇을 얻었는지 확인할 수 있는 것 같아요.프로젝트를 함께한 팀원과의 회고는 서로의 감정을 공유하는 자리이기도 해요. 함께 일하는 팀원의 생각을 조금 더 이해하고 팀워크를 다지는 계기가 되기도 하죠.보니(Product Mananger) 🦊스프린트 회고와 최종 회고의 역할이 조금 다르다고 생각해요. 스프린트 회고(주간 회고)는 프로젝트 자체가 잘 운영되기 위한 회고에 가까워요.최종 회고는 팀이나 개인 차원에서의 장기적인 발전을 위한 회고인 것 같아요. 서로의 생각을 공유하고 앞으로 더 잘하기 위한 반성이 포함된 느낌?출처: MBC 무한도전  회고를 '잘' 하려면?1. 팀이나 개인에 맞는 회고 방식을 모색하는 시간이 필요해요.인프랩은 보통 KPT 방식으로 회고를 진행해요.빠삐코(Front-end Engineer) 🍦회고를 잘하기 위해서 개인적으로 여러 시도를 해봤는데요. 매일 업무 일지를 작성하는 게 도움이 많이 됐어요. 간단하게만 작성해둬도 나중에 전체 회고를 할 때 도움이 되는 경우가 많았어요. 프로젝트 과정에서 발생했던 문제를 시간이 지나면 잊기도 하는데, 업무 일지가 그런 것들까지 떠올리게 해줬습니다. 2. 프로젝트의 목표를 명확하게 정해두는 게 좋아요.엠제이(Product Designer) Ⓜ️처음에 프로젝트의 목표를 명확하게 정해두는 게 꼭 필요하다고 생각해요. 목표가 명확해야 회고할 때의 기준이 생기고, 업무에 대한 동기부여가 되는 것 같아요. 프로젝트가 목표에 맞게 잘 진행이 되었는지를 중심으로 이야기하니까 회고하기도 수월했어요.중요한 점은 목표를 반드시 팀원 다같이 논의해서 정하는 거예요. 3. 프로젝트에 대한 설계나 계획이 잘 되어 있어야 해요.보니(Product Mananger) 🦊프로젝트 동안 팀원 각자 역할에 대한 계획을 세우는데, 그 계획을 잘 세워두면 좋은 회고가 가능한 것 같아요. 프로젝트 동안 팀원 개개인이 매일 어떤 일을 했는지 스스로 알고 있어야 회고할 거리가 생기더라고요. 그렇지 않으면 느낌이나 기억에 의존한 감정적인 회고에만 그칠 수 있다고 생각해요. 4. 프로젝트를 함께한 팀원에 대한 존중과 배려가 필요해요.비케이(Product Designer) ⚒️회고할 때 감정이 아닌 행동 중심으로 이야기하라는 말에 공감해요.빠삐코(Front-end Engineer) 🍦서로를 존중하되, 솔직하게 말하는 태도가 필요하다고 생각해요. 서로를 존중하느라 진짜 하고 싶은 이야기를 놓치면 안되니까요.출처: MBC 아빠! 어디가?회고에 대한 정답이 정해져 있는 건 아니지만, 회고의 목적이 업무의 효율성과 앞으로의 성장에 있다는 점은 누구나 공감할 것 같아요.여러분은 회고를 해야 하는 이유가 무엇이라고 생각하시나요? 회고를 잘하기 위한 방법은 뭐라고 생각하시나요? 회고에 대한 다양한 의견을 댓글로 남겨주세요! 회고 문화를 더 깊게 다룬 글을 읽어보고 싶다면?[개발자의 공유 문화 이모저모 (2) 회고 문화] 읽어보기 >> 인프랩 팀원이 작성한 다양한 회고록을 살펴보고 싶다면?[인프랩 실Log] 읽어보기 >>

개발 · 프로그래밍 기타회고회고문화애자일

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기발자국회고과제미션

[ 쿠버네티스 어너더 클래스 ] 1 주차 발자국

강의 수강 내용월 - OT, Q & A화 - Sprint1 컨테이너 한방 정리수 - 무게감 있게 쿠버네티스 설치목 - 실무에서 느껴 본 쿠버네티스 정말 평한 이유금 - Object를 그려보며 쿠버네티스 이해 강의 칭찬하고 싶은 점칭찬하고 싶었던점 헤이해졌었지만 다시 마음 잡아 공부를 시작한거 복습 글을 모두 작성했던 것 칭찬하고 싶습니다.아쉬웠던 점필기로 그려봐서 나의 것으로 확실히 만들려 했는데 글이나 그림 그리는데 시간이 부족해서 다 하지 못했습니다.그리고 시간이 상당히 오래 걸려서 비효율적이였습니다.보안해야 될 점쓸데 없는데 힘 빼지 말고(그림이나 글 직접 그리거나 쓰기) 공부하는 시간 딱 집중해서 수업을 듣고 복습 내용 바로 정리 해서 글 올리기, 발자국을 일주일 다되서 작성하지 말고 복습 그날그날 하면서 회고 때 넣을 내용 생각날 때 적어두기(생각 이 안남) 미션 미션 해결 하는 과정VM & K8S 설치 스크립트 도중에 종료처음에 스크립트가 시간이 꽤나 오래 걸리는지 모르고 렉이 걸려서 안되는지 알았다. ctrl + c 로 중지하게 되었다.그랬더니 남아 있는 베이그란트 프로세스가 컴퓨터 관리자에 남아 있어서 vagrant up 명령이 듣지를 않았다.검색 후에 돌아가고 있는 vagrant 프로세스를 다 종료, VM 에 남아 있는 모든 vagrant 관련 디렉터리를 삭제 후에다시 설치 했더니 문제가 해결이 되었다. 프로메테우스 모니터링 보드 오류프로메테우스 모니터링 보드에 모든 데이터가 No data 로 되어 있어서 Locky 가 k8s 클러스터 상의 컴포넌트들의 메트릭스를 수집 못하고 있나해서 Locky를 삭제 후 다시 깔았다. But 증상은 똑같았고 프로메테우스와 Locky를 모두 삭제 후 다시 설치 했더니 정상적으로 K8s의 파드 디폴로이 먼트 등 컴포넌트들의 모니터링이 가능해졌다.  

k8s회고발자국컨테이너VMk8s클러스터설치

suover

인프런 워밍업 클럽 스터디 4기 - CS 전공지식 1주차 발자국

그림으로 쉽게 배우는 자료구조와 알고리즘 (심화편)만들면서 쉽게 배우는 컴퓨터 구조강의와 함께한 인프런 워밍업 클럽 스터디 4기 - CS 전공지식 (자료구조, 알고리즘, 컴퓨터구조)1주차 발자국 입니다.학습 내용 요약이번 주에는 두 가지 강의를 통해 자료구조·알고리즘과 컴퓨터 구조의 큰 틀을 살펴보며, 이론과 실습을 병행했습니다.그림으로 쉽게 배우는 자료구조와 알고리즘 (심화편)크게 트리 구조와 이진 탐색 트리(BST), 그리고 그 위에 균형을 더한 AVL 트리를 다뤘습니다.초반에는 "트리와 이진 트리 개념"으로 부모·자식 노드 간 관계를 복습했고, 이어서 BST의 삽입·검색·삭제 과정을 훑어보았습니다.마지막으로 AVL 트리 개념과 구현(보조 함수, 삽입, 삭제)을 익히며, "왜 균형 인자가 중요한지"와 "삽입/삭제 후 어떻게 회전으로 높이를 조정하는지"를 이해했습니다.만들면서 쉽게 배우는 컴퓨터 구조컴퓨터 구조 전반(블랙박스 관점, CPU·메모리·주변 장치)과 함께, 불 대수 및 논리 게이트 설계로 넘어가 간단한 하드웨어 시뮬레이션을 해보았습니다.먼저 "컴퓨터 구조를 배우는 이유"와 명령어가 메모리→CPU→다시 메모리로 흐르는 과정을 개념적으로 파악했고, CPU 내부와 메모리 계층도 간단히 살펴봤습니다.이어서 불 대수 기본 연산과 성질을 학습한 뒤, 직접 Logisim을 설치해 NAND/NOT/AND/OR/XOR 게이트를 조합해 보는 실습을 경험했습니다.또 10진수↔2진수 변환, 16진법, 빅·리틀 엔디안, 2의 보수 음수 표현 같은 비트 단위 개념을 짚으며, 디지털 회로의 밑바탕이 되는 이진 표현 방식을 정리했습니다.미션🎯 자료구조·알고리즘 미션1: GarbageCollector 클래스 구현목표: AVL 트리로 빈 메모리 블록을 관리해, 요청 크기와 정확히 일치하는 블록이나 그보다 큰 값 중 최소값을 찾아 반환·삭제하도록 한다.코드import { AVLTree } from "../../avlTree/avlTree.mjs"; class GabageCollector { constructor() { this.avlTree = new AVLTree(); } // 빈 메모리 블록 삽입 insertFreeMemory(size) { this.avlTree.root = this.avlTree.insert(this.avlTree.root, size); } // 요청 크기 이상인 블록 검색 searchFreeMemory(requestSize) { let current = this.avlTree.root; let candidate = null; while (current) { const nodeSize = current.getData(); if (nodeSize === requestSize) { // 정확히 같은 크기를 찾으면 즉시 반환 candidate = current; break; } if (nodeSize > requestSize) { // 후보로 저장한 뒤, 더 작은 후보를 찾기 위해 왼쪽 서브트리 탐색 candidate = current; current = current.getLeftSubTree(); } else { current = current.getRightSubTree(); } } return candidate; } // 반환된 블록(크기) 삭제 releaseFreeMemory(size) { this.avlTree.root = this.avlTree.remove(this.avlTree.root, size); } } // 실행 const gc = new GabageCollector(); console.log("========== 빈 메모리 영역 초기화 =========="); gc.insertFreeMemory(64); // 빈 64바이트 삽입 gc.insertFreeMemory(48); // 빈 48바이트 삽입 gc.insertFreeMemory(87); // 빈 87바이트 삽입 gc.insertFreeMemory(13); // 빈 13바이트 삽입 gc.insertFreeMemory(102); // 빈 102바이트 삽입 gc.insertFreeMemory(34); // 빈 34바이트 삽입 gc.insertFreeMemory(61); // 빈 61바이트 삽입 gc.insertFreeMemory(40); // 빈 40바이트 삽입 gc.insertFreeMemory(6); // 빈 6바이트 삽입 let freeMemory1 = gc.searchFreeMemory(64); // 64바이트 메모리 console.log(freeMemory1); if (freeMemory1) { gc.releaseFreeMemory(freeMemory1.data); } let freeMemory2 = gc.searchFreeMemory(42); // 48바이트 메모리 획득 console.log(freeMemory2); if (freeMemory2) { gc.releaseFreeMemory(freeMemory2.data); }실행 요약const gc = new GarbageCollector(); 빈 블록 [64, 48, 87, 13, 102, 34, 61, 40, 6]을 insertFreeMemory(...)로 순차 삽입 searchFreeMemory(64) → 정확히 64 노드를 찾아 반환 → releaseFreeMemory(64) 삭제 searchFreeMemory(42) → 42와 동일한 값이 없어 “42보다 큰 값 중 최소(48)” 반환 → releaseFreeMemory(48) 삭제 회고AVL 트리의 삽입·삭제 시 "LL·RR·LR·RL 회전"이 실제로 동작하는 것을 확인하며, 균형 인자의 중요성을 체감했습니다. 삽입, 삭제, 검색이 모두 로그 시간에 동작하기 때문에, 빈 메모리 블록이 많아질 때도 성능을 보장할 수 있다는 점을 체감했습니다. "정확히 같은 크기가 없을 때, 그보다 큰 값 중 최소값을 찾는 후보(candidate)" 로직을 루트부터 내려가며 올바르게 업데이트해야 합니다.    🔗자료구조·알고리즘 미션1 블로그 링크🎯 컴퓨터 구조 미션1 (총 5개)미션 1. 4입력 논리 연산 진리표 작성내용: 4개의 입력(A, B, C, D)에 대한 AND, OR, NAND, NOR, XOR 진리표를 총 16조합으로 작성실행A B C D를 0000 → 0001 → … → 1111 순서로 나열 각 연산(AND, OR, NAND, NOR, XOR)의 출력(Q)을 직접 채워 넣음느낀 점2진수 순서대로 입력 조합을 정리하니 빠짐없이 작성할 수 있었고, XOR처럼 "1 개수 홀짝 판정" 연산은 중간에 헷갈려도 정리 순서를 엄수하면 실수를 줄일 수 있었다.미션 2. 불 방정식 간략화내용: 아래 네 개의 불 방정식을 단계별로 법칙(아이디엠포턴트, 드모르간, 흡수 등)을 적용해 간략화했다.A( (BB)’ + 0A) + (CC)’ = (AB’) + C’ (B’B’) + (AD’ + (CA)’)D = B’ + (DC’) + (DA’) (A’B) + B(B1 + BC) = B B’(1C + BD) + DB = (B’C) + (DB) 느낀 점드모르간·흡수법칙을 차례로 적용하며 "복잡해 보이던 식이 단번에 간결해지는 과정"이 인상적이었다.단계별로 어떤 법칙을 먼저 적용해야 가장 효율적으로 축약되는지 아직 직관이 부족해, 다음주엔 다양한 예제를 풀어 보며 연습할 예정이다.미션 3. 2진수를 10진수로 변환내용: 다음 네 개의 2진수를 10진수로 바꿔 보았다.110111₂ = 55₁₀ 10000001₂ = 129₁₀ 11111100000₂ = 2016₁₀ 101010₂ = 42₁₀실행 각 2진수를 우측 끝 비트부터 자리값(2⁰, 2¹, 2², …)을 곱해 합산 예: 11111100000₂ = 1·2¹⁰ + 1·2⁹ + … + 1·2⁵ + 0·… = 1024 + 512 + 256 + 128 + 64 + 32 = 2016느낀 점자릿값을 일일이 계산하는 것은 번거롭지만, "가장 큰 2의 거듭제곱부터 차례로 빼 나가는 방식"으로 익히니 긴 이진수도 비교적 빠르게 변환할 수 있었다.미션 4. 10진수를 2진수로 변환내용: 다음 네 개의 10진수를 2진수로 바꿔 보았다.10₁₀ = 1010₂ 27₁₀ = 11011₂ 86₁₀ = 1010110₂ 516₁₀ = 1000000100₂실행 나눗셈→나머지 반복: 예를 들어 86 ÷ 2 = 43, 나머지 0 → 43 ÷ 2 = 21, 나머지 1 → … → 최종 이진수 역순 또는 "가장 큰 2의 거듭제곱(2⁶=64)부터 빼 나가기" 방식으로 빠르게 도출느낀 점처음엔 헷갈렸지만, "2ⁿ 이하 최대값을 찾아서 빼고, 나머지로 또 반복"하는 방식이 머릿속에 각인되자 더 긴 수도 금방 변환할 수 있었다.미션 5. 불 방정식을 Logisim으로 회로 구현내용: 불 방정식을 logisim을 이용해 회로 만들기 실행(B′·C) + (D·B)B → NOT → B′AND1: (B′, C)AND2: (D, B)OR: (AND1, AND2) → 출력(A·B′) + CB → NOT → B′AND: (A, B′)OR: (AND, C) → 출력B′ + (D·C′) + (D·A′)B → NOT → B′ (첫 OR 입력)C → NOT → C′, A → NOT → A′AND1: (D, C′)AND2: (D, A′)OR( B′, AND1, AND2 ) → 출력대표 입력별 검증(B′·C)+(D·B) → B=0,C=1,D=0 → OUT=1, B=1,C=0,D=1 → OUT=1, B=1,C=1,D=0 → OUT=0(A·B′)+C → A=0,B=0,C=1 → OUT=1, A=1,B=1,C=0 → OUT=0B′+(D·C′)+(D·A′) → A=0,B=0,C=0,D=0 → OUT=1, A=1,B=1,C=0,D=1 → OUT=1느낀 점Logisim을 통해 "진리표상의 연산이 실제 게이트 단위 회로에서 어떻게 동작하는지"를 직접 눈으로 확인할 수 있었다.회로가 복잡해질수록 배선이 겹치는 문제가 있었으나, 다음에는 모듈 단위(예: (B′·C) 회로 → (D·B) 회로 → OR)로 나눠서 단계별로 검증하려 한다. 🔗컴퓨터 구조 미션1 블로그 링크회고이번 주에는 AVL 트리와 논리 회로 설계를 함께 학습했습니다.AVL 트리 구현: 삽입·삭제 시 LL·RR·LR·RL 회전을 거쳐 균형을 유지하는 과정을 직접 코드로 확인했습니다. "정확히 같은 값이 없으면 그보다 큰 값 중 최소값을 찾는 로직"을 구현하면서, 균형 인자를 어떻게 업데이트해야 하는지 확실히 알게 되었습니다.논리 회로 설계: 4입력 진리표 작성부터 불 방정식 간략화, Logisim 회로 구성까지 순서대로 진행했습니다. 직접 회로를 만들어 입력을 바꿔 볼 때마다 결과가 즉시 바뀌는 모습을 보면서, 이론이 실제 게이트 동작으로 이어진다는 점이 와닿았습니다.스스로 칭찬하는 점이론과 실습 병행AVL 트리 코드와 Logisim 회로를 동시에 구현하며, 배운 내용을 손으로 직접 확인한 점이 좋았습니다.진리표 정리법 습득2진수 순서대로 진리표를 빠짐없이 작성하면서, 체계적인 정리 방법을 제대로 익혔습니다.아쉬웠던 점 & 보완회로 배선 정리 부족Logisim에서 선이 겹치다 보니 가끔 헷갈렸습니다.→ 다음에는 작은 모듈로 나눠 단계별로 검증하고, 마지막에 합치는 방식을 사용하겠습니다. 개념 정리 자료 부족AVL 트리나 불 대수 법칙을 공부할 때, 머릿속으로만 이해하고 따로 요약해 두지 않아 복습할 때 헷갈리는 부분이 있었습니다.→ 학습 중에는 핵심 개념을 노트에 간단히 정리하여, 부족한 부분을 바로 찾아볼 수 있도록 하겠습니다.마치며이번 주차는 이론을 코드와 회로로 연결해 보는 경험을 했습니다. 다음 주에도 부족했던 부분을 보완하며 차근차근 학습을 이어가겠습니다! 감사합니다!

알고리즘 · 자료구조인프런워밍업클럽스터디CS전공지식자료구조컴퓨터구조발자국회고4기

바다다다

[인프런 워밍업 스터디 클럽 3기 FE] 3주차 발자국

수강강의1: 따라하며 배우는 자바스크립트 A-Z수강강의2: 따라하며 배우는 리액트 A-Z[19버전 반영]Tip: 워밍업 스터디 클럽을 참여하게 되면 [할인쿠폰]으로 강의를 할인 된 가격에 수강할 수 있다! 3주차 강의 수강3주차를 되돌아 보며..이번주 강의는 전반적으로 React와 친해지는(?) 시간을 가지게 된거 같다.React로 실제 화면을 구현해보고 각종 함수와 툴들을 사용해서 개발의 편의성을 높이는 것을 체감할 수 있었다.특히 주어진 미션과제를 수행하면서 React의 동작성, 상태관리에 대해서 더 깊게 알 수 있을 시간이 되었다.사실 이번주는 강의를 많이 듣지 못했다ㅠ 현업과 병행하다 보니 퇴근하고 강의 듣는다는 게 쉬운 일은 아니다. 특히 이번주는 시험검증 기간이라 이슈가 조금 많이 발생해서 야근도 잦고 그랬다... (모든 직장인, 학생 분들 화이팅입니다ㅠ 열심히 살아가봐요!) 그리고... 미션과제도 밀려서 주말에 몰아서 구현을 했다.미션과제를 수행하면서 중간중간 필요한 지식들은 강의를 들으면서 기록하며 학습을 진행했다.Javascript 강의 때와는 다르게 React 는 실습위주 이다보니 강의 내용을 정리한게 많지는 않다. (강의보면서 코딩따라 해보며 기능을 확인하고 미션에 적용하려고 했다)강의 내용을 다시 정리해보며..(주로 개인적으로 새롭게 알게된 것, 다시 기억해야하는 것들 위주로 정리했다)리액트는 프레임워크가 아니라 라이브러리React는 라이브러리, Vue, Angular는 프레임워크왜 라이브러리? 리액트는 전적으로 UI를 렌더링하는 것에 관여하기 때문상태관리, 라우팅, 테스트 등등을 위해 다른 라이브러리가 추가적으로 필요함 (vue, angular는 이런 것들이 이미 포함되어 있음)프레임워크: 어떠한 앱을 만들기위해 필요한 대부분의 것을 가지고 있는 것라이브러리: 어떠한 특정 기능을 모듈화 해놓은 것프레임워크는 라이브러리의 집합리액트 컴포넌트 - Component리액트 앱을 이루는 최소한의 단위, 여러 컴포넌트를 조합하여 하나의 페이지가 완성되는 것클래스형 컴포너트 - class component함수형 컴포넌트 - funcional component브라우저가 그려지는 원리와 가상돔리액트의 주요 특징 중 하나가 가상돔을 사용하는 것이다웹 페이지 빌드 과정 (CRP) - Critical Render Pathbrowser가 서버에서 페이지에 대한 HTML문서를 응답으로 받고, 해당 문서를 읽는다. 그 후 스타일을 입히고 뷰포트에 표시하게 된다DOM tree생성 → Render Tree생성(화면에 표시되는 모든 노드의 콘텐츠 및 스타일 정보를 포함) → Layout(reflow) → Paint(화면에 그리기)화면에서 DOM에 변화가 생기면 Render Tree부터 다시 랜더링 해야한다는 문제점!! → 비효율적→ 가상돔: 실제 DOM을 메모리에 복사해준 것데이터가 바뀌면 가상돔에 랜더링되고 이전에 생긴 가상돔과 비교해서 바뀐 부분만 실제 돔에 적용 시킴. 바뀐 부분을 찾는 과정을 diffing이라고 부르고, 바뀐 부분만 실제 돔에 적용시켜주는 것을 재조정(reconciliation)이라 한다.리액트 구조 살펴보기이름이 수정되면 안되는 파일public/index.html → page템플릿src/index.js → 자바스크립트의 시작점SPA는 어떻게 가능하게 되나?HTML5에서 History API를 사용해서 가능하게 함. (실제 react-dom에서 사용하는 방식)JSXJavascript syntax extension자바스크립트의 확장 문법이다React에서 의무적으로 사용하는 것 XJSX는 babel을 통해 변환된다컴포넌트에 여러 엘리먼트 요소가 있다면 반드시 부모요소 하나로 감싸줘야한다.JSX Keykey는 리액트가 변경, 추가 또는 제거된 항목을 식별하는데 도움이 된다. 요소에 안정적인 ID를 부여하려면 배열 내부의 요소에 키를 제공해야 한다 (key값을 기준으로 바뀐 가상돔을 감지한다)key에는 unique한 값을 넣어주자. index는 비추!React State컴포넌트의 렌더링 결과물에 영향을 주는 데이터를 갖고있는 객체이다. state가 변경되면 컴포넌트는 리랜더링(re-rendering)된다. 또한 state는 컴포넌트 안에서 관리된다.React HooksReactConf 2018에서 발표된 class없이 state를 사용할 수 있는 새로운 기능리액트의 생명주기react hooks를 통해 함수형 컴포넌트에서도 생명주기를 사용할 수 있게 데이터를 가져와서 컴포넌트 시작하자 마자 API호출하고 많은 부분을 사용할 수 있게되었다→ 코드가 간결해짐 (useEffect를 통해 componentDidMount, componentDidUpdate, componentWillUnmount를 다 수행해줌)HOC(higher order component)를 Custom react hooks로 대체해서 너무 많은 wrapper컴포넌트를 줄이게 된다.→ HOC: 화면에서 재사용 가능한 로직만을 분리해서 component를 만들고, 재사용 불가능한 UI와 같은 다른 부분들은 parameter로 받아서 처리하는 방식 (wrapper가 많아지면 데이터의 흐름을 파악하기 어려워짐)State, Propsstate해당 컴포넌트 내부에서 데이터를 전달할 때state는 mutable 변경가능하다state 이 변하면 re-render된다props상속하는 부모컴포넌트에서 자식 컴포넌트로 전달한다읽기전용으로 작년 컴포넌트 입장에서는 변하지 않는다. (변하게 하고자 하면 부모 컴포넌트에서 state를 변겨해줘야한다)구조 분해 할당(Destructuring)배열이나 객체의 속성을 해체하여 그 값을 개별 변수에 담을 수 있게 하는 JS표현식클린코드를 위해 객체, 배열에 대해 구조 분해 할당Redux상태 관리 라이브러리redux데이터는 하나의 방향으로만 흐른다flow: react component에서 어떠한 이벤트가 발생하면 → Action 객체(어떤 작업을 수행할 것인지 description을 담고 있음)를 통해 reducer함수에게 액션을 발생하라고 한다.(dispatch) → reducer함수에서 로직 처리 → store내부 상태 업데이트: redux store state update → 새롭게 업데이트 된 상태를 이용해서 다시 렌더링 → component re-renderingRedux Tookitredux로직을 작성하기 위한 공식 권장 접근 방식 강의 내용을 들으면서 아쉬웠던 점..앞서 언급한 대로 이번주에는 강의보다는 실습 과제에 조금 더 초점을 두었다. 미션을 수행하면서 모르는 부분을 강의에 가서 찾아 듣고 다시 구현하고 하는 식으로 학습을 진행했다.React는 이론 보다는 실제 사용하고 구현해보는게 더 빠르게 성장하는거 같다. 특히 상태관리, 페이지 라우터 등 강의에서도 알려주시지만, 미션과제를 수행하면서 직접 기능을 동작시킬 수 있게 구현하는게 더 깊이 이해할 수 있었던 거 같다.아쉬운점은ㅠ 강의를 많이 못 들었다는거? 수료식까지 아직 시간이 조금 남았으니까 남은 강의도 열심히 들어보겠다.3주차 미션 완료React미션 두 개(포켓몬, 쇼핑몰)를 토요일 벼락치기로 수행했다.간단하다고 생각했는데 React가 아직 익숙하지 않아 중간중간 오류가 많이 발생했었고, 특히 프론트엔드는 화면이 바로 눈에 보이다 보니까 CSS 스타일 적으로 내맘대로 적용되지 않아서 화가났다(?). 그래도 tailwindcss에서 만들어진 component, icon들을 활용해서 미션을 수행완료할 수 있었다.React - Mission3: 포켓몬 도감 앱 만들기Demo: https://pokemon-mission-3.netlify.app/Source Code: https://github.com/rim0703/React-study/tree/react/mission3/React/3-pokemonpokeAPI 라는 오픈소스 API를 제공하는 곳이 있어서 외부API를 호출해서 구현했다API문서가 얼마나 중요한 역할을 하는지 이번 미션에서 깊이 깨달았다.. ㄷㄷ(백엔드 화이팅)(지연로딩 적용) 사용자의 화면 로딩 속도를 고려하면 첫 화면에 20개씩 불러오도록 했고, 스크롤이 하단에 도달하면 추가로 호출하도록 구현했다.(Trick) 시연화면에서 로딩이 출력되는데 사실 화면이 너무 빨리 렌더링 완료돼서 로딩이 보이지 않아 로딩 동글뱅이를 보기 위해 setTimeout으로 1초 지연을 넣었다 ^^;;(검색창) 조금 버그가 있다. 페이지를 영문이 아닌 한글로 만들다보니 검색도 한글로 하도록 했는데 상태를 상위-하위 컴포넌트 간에서 전달하다 보니 조금 어려웠다. 또한 API문서 기본은 영문으로 제공해서 영문과 한글을 매칭시키는 별도의 작업도 필요했다. redux를 사용해서 검색창을 상단 navigation바에 위치해보려고 한다.  React - Mission6: 리덕스를 이용한 쇼핑몰(버거몰) 앱 만들기Demo: https://burger-mall-mission-6.netlify.app/Source Code: https://github.com/rim0703/React-study/tree/react/mission6/React/6-shopping-mall1주차 미션 내용에서 언급했듯이.. JS강의에서 만들었던 메뉴 화면을 그대로 따와서(CSS공부가 아니니까^^) 쇼핑몰과 비슷한 기능들이 있는 버거몰을 만들어보았다.(컴포넌트화) HTML,JS,CSS로 구현된 내용을 React로 옮겼고, 원래 파일 1개씩 정의된 내용을 React Component로 분류하였다.(장바구니-localstorage) DB를 별도로 연동된게 아니여서 localstorage를 활용해서 장바구니를 구현하였다.(Redux) 리덕스 강의 내용에 기반한 프로젝트이지만 Redux강의를 듣고 이해가 잘되지 않아서 우선 기본 방식으로 앱을 구현하였다. Redux공부를 조금 더 하고 리팩토링을 진행할 예정이다. 마지막으로 3주차 회고이제 수료까지 얼마남지 않았는뎅,, 남은 시간동안 강의를 열심히 들어야겠다.너무 빡빡한 일정이었지만 3주차까지 버텨오고 React에 대해 친해질 수 있는 시간이되어서 뿌듯하다.현업에서 Vue를 사용하다 기술 전환을 위해 React를 도입한다고 해서 급하게 스터디에 신청해서 힘들지만 조금만 더 버티면되지 않을까? 직장과 병행하니.. 이게 쉬운일은 아닌거 같다. 모든 직장인들 화이팅입니다!흑흑.. 미션 기능 중에 동작하지 않는게 있어서 수정하고 다시 배포하고 이제 자러간당ㅠㅠ다음주는 더 성장한 내가 되어 있기를!! 모두 완주까지 화이팅입니다~(2025.03.23 새벽 03:26)<끝>

프론트엔드워밍업스터디3기프론트엔드미션JSJavascriptReactRedux회고

suover

인프런 워밍업 클럽 스터디 2기 - 백엔드 프로젝트 4주차 발자국

입문자를 위한 Spring Boot with Kotlin - 나만의 포트폴리오 사이트 만들기강의와 함께한 인프런 워밍업 클럽 스터디 2기 - 백엔드 프로젝트 (Kotlin, Spring Boot) 4주차 발자국 입니다. 강의 수강이번 주에는 Spring Boot와 JPA를 사용한 뷰 개발 및 Thymeleaf를 활용한 프론트엔드 템플릿 작업에 중점을 두었습니다. 주로 Bootstrap 템플릿을 가져와 활용하고, Thymeleaf의 fragment 기능을 사용하여 재사용 가능한 HTML 구조를 만들었으며, 배포 작업까지 진행했습니다.주요 학습 내용Bootstrap 템플릿 가져오기 및 적용Bootstrapmade 사이트에서 Admin 템플릿을 다운로드하여 프로젝트에 적용했습니다.템플릿 파일을 프로젝트의 resources 디렉토리에 추가하고, 필요한 CSS, JS 파일들을 적용했습니다.Thymeleaf와 프론트엔드 분리 작업Thymeleaf의 fragment 기능을 활용해 HTML 구조를 모듈화하였습니다. head, header, footer 등 여러 공통 요소를 별도의 fragment로 분리하여 재사용성을 높였습니다.타임리프 문법을 통해 동적으로 페이지를 구성하고 유지보수가 용이하도록 개선했습니다.프로젝트 배포 작업Docker를 이용해 MySQL을 컨테이너로 실행하고, Spring Boot 애플리케이션을 Docker 이미지로 빌드하여 배포했습니다.Nginx를 사용해 80포트와 8080포트를 연결하여 클라이언트 요청을 처리하도록 설정했습니다.Let's Encrypt를 이용해 HTTPS 인증서를 설정하고, 웹사이트를 HTTPS로 안전하게 접근할 수 있도록 구성했습니다. 회고이번 주는 Thymeleaf와 Bootstrap을 활용하여 프론트엔드 작업을 진행하며, 뷰를 모듈화하고 효율적으로 구성하는 방법을 배울 수 있었습니다. 특히 Thymeleaf의 fragment 기능은 HTML 코드의 재사용성과 유지보수성을 크게 개선해 주어 프로젝트의 구조를 더욱 체계적으로 만들 수 있었습니다. 또한, 직접 템플릿을 가져와 수정하고 적용하는 과정을 통해 프론트엔드 개발 역량을 키울 수 있었고, Docker와 Nginx를 이용한 배포 작업을 통해 실제 서비스 환경에서의 배포 경험도 쌓을 수 있었습니다.칭찬할 점Thymeleaf를 사용하여 공통 레이아웃을 fragment로 분리하여 재사용성을 높인 점.Bootstrap 템플릿을 프로젝트에 무리 없이 적용하고, 필요한 수정 작업을 성공적으로 수행한 점.Docker와 Nginx를 이용한 배포 과정을 원활히 진행한 점.아쉬웠던 점 및 보완할 점Thymeleaf 문법에 대한 익숙함이 아직 부족하여 일부 동적 작업에서 어려움을 겪었습니다. 추가적인 연습과 학습을 통해 더 익숙해질 필요가 있습니다.프론트엔드 요소의 커스터마이징에 있어 부족함을 느꼈으며, 좀 더 세밀한 스타일 수정 방법을 연구하고자 합니다. 미션이번 주 미션은 삽입, 수정, 삭제 REST API를 개발하고, 이를 검증하는 테스트 코드를 작성하는 것이었습니다. API는 각 기능별로 POST, PUT, DELETE 요청을 처리하도록 설계하였으며, 모든 케이스에 대해 성공적으로 테스트를 완료했습니다.미션 과정게시글 삽입 API 설계 (POST /api/posts)새로운 게시글을 작성하고, 작성된 데이터를 JSON 형태로 반환하도록 설계했습니다.게시글 수정 API 설계 (PUT /api/posts/{postId})특정 게시글을 수정하고, 수정된 결과를 반환하는 기능을 구현했습니다.게시글 삭제 API 설계 (DELETE /api/posts/{postId})특정 게시글을 삭제하고, 삭제 후 상태 코드 204 No Content를 반환하도록 했습니다.테스트 코드 작성 및 검증각 엔드포인트에 대해 최소 3개의 테스트 케이스를 작성해, 다양한 상황에서의 시스템 동작을 검증했습니다.필수 값이 누락된 경우, 잘못된 ID로 요청했을 때의 예외 처리 등도 함께 테스트하여 안정성을 확보했습니다.  미션 과정추가적으로 가상 프로필을 나의 프로필로 바꾸기 미션과 배포한 프로젝트 공유하기 미션까지 모두 마무리하며, 스터디의 모든 미션을 마무리 하였습니다. 이 발자국을 끝으로 모든 미션과 발자국, 강의 수강 100% 까지 마무리 하며, 스터디를 성공적으로 완주하였습니다. 회고이번 미션을 통해 RESTful한 API 설계 및 테스트의 중요성을 다시 한번 체감할 수 있었습니다. 또한, Thymeleaf를 활용해 프론트엔드를 모듈화하고 코드의 재사용성을 높이는 작업이 실제 프로젝트에서 어떻게 활용될 수 있는지 직접 경험할 수 있는 시간이었습니다. 배포 작업을 통해 실제 서비스 환경에서 발생할 수 있는 문제들을 다루며, 더 나은 품질의 소프트웨어를 개발하기 위한 실무 경험을 쌓을 수 있었습니다. 앞으로 JPA 연관 관계에 대한 이해를 깊이 있게 다지고, 테스트 코드의 가독성을 더욱 높이는 방법을 연구하여 더 나은 품질의 소프트웨어를 개발해 나가겠습니다.

백엔드인프런워밍업클럽스터디백엔드프로젝트발자국회고SpringBoot

suover

인프런 워밍업 클럽 스터디 2기 - 백엔드 프로젝트 3주차 발자국

입문자를 위한 Spring Boot with Kotlin - 나만의 포트폴리오 사이트 만들기강의와 함께한 인프런 워밍업 클럽 스터디 2기 - 백엔드 프로젝트 (Kotlin, Spring Boot) 3주차 발자국 입니다. 강의 수강이번 주에는 실습 중심으로 Spring Boot와 JPA를 사용한 컨트롤러 개발과 API 설계를 진행했습니다.API 테스트 코드를 작성해보고, Thymeleaf를 활용한 프론트 개발 작업도 수행했습니다.주요 학습 내용Spring MVC와 JPA를 활용한 컨트롤러 개발PresentationApiController 및 PresentationViewController에서 다양한 API 엔드포인트를 구현했습니다.JPA를 통해 데이터베이스와 상호작용하며 데이터를 처리하는 방법을 학습했습니다.Thymeleaf 프론트엔드 개발템플릿을 활용하여 데이터를 프론트엔드에 전달하고 뷰를 생성했습니다.커스터마이징을 통해 유지 보수가 용이한 뷰를 구성했습니다.컨트롤러 테스트 코드 작성API 테스트 코드를 작성하여 정확하게 동작하는지 검증했습니다.실제 API가 예상대로 동작하는지 검증하는 방법을 배웠습니다. 회고이번 주는 Spring Boot와 JPA를 활용한 컨트롤러 개발과 API 설계, 그리고 테스트 코드 작성까지 다양한 실습을 통해 실질적인 개발 경험을 쌓는 데 집중한 한 주였습니다. 특히, 데이터를 조회하고 처리하는 API를 설계하면서 RESTful 구조에 대해 깊이 고민했고, 테스트 코드 작성이 얼마나 중요한지 다시 한번 깨닫게 되었습니다. 실습을 통해 새로운 개념들을 익히고, 실제 프로젝트에 적용해보며 기술적 성장을 이뤘습니다.칭찬할 점테스트 코드를 작성하여 API의 주요 기능들을 효율적으로 검증했습니다.Thymeleaf 템플릿 커스터마이징을 통해 프론트엔드를 구성하고, 유지 보수가 용이하도록 최적화했습니다.아쉬웠던 점 및 보완할 점Thymeleaf 문법에 대한 이해가 부족하여 복잡한 작업에서 어려움이 있었습니다.테스트 코드를 더 간결하고 효율적으로 작성하기 위해 리팩토링 기법을 학습할 계획입니다.미션이번 주 미션은 게시글 조회 REST API를 개발하고, 이를 검증하기 위한 테스트 코드를 작성했습니다.API는 전체 게시글 조회와 특정 게시글 조회 두 가지 기능을 제공합니다. 미션 과정전체 게시글 조회 API 설계GET /api/posts 엔드포인트에서 전체 게시글을 조회하고, JSON 형식으로 데이터를 반환합니다.특정 게시글 조회 API 설계GET /api/posts/{postId}로 특정 게시글 ID를 기반으로 게시글을 조회하여 반환하는 기능을 구현했습니다.테스트 코드 작성 및 검증각 엔드포인트에 대해 단위 테스트를 작성하여 모든 기능이 예상대로 동작하는지 검증했습니다.특히 게시글이 없는 경우와 같은 예외 상황을 처리하는 테스트도 포함시켜 로직의 완성도를 높였습니다.  회고이번 미션을 통해 RESTful한 API 설계의 중요성을 다시 한번 느꼈습니다. 엔드포인트를 직관적이고 간결하게 설계하기 위해 많은 고민을 했으며, 테스트 코드를 작성하며 API 동작을 검증하는 과정에서 테스트의 중요성을 다시 한번 실감할 수 있었습니다.아쉬운 점 및 보완할 점테스트 코드의 가독성과 효율성을 높이기 위해 추가적인 리팩토링이 필요합니다.JPA의 복잡한 연관 관계 처리에서 어려움을 겪었으며, 이러한 부분을 더 명확하게 이해하기 위해 추가적인 학습이 필요합니다.이번 주의 학습과 미션을 통해 부족했던 부분들을 확인할 수 있었고, 앞으로도 지속적인 학습과 실습을 통해 문제를 개선해 나갈 계획입니다. 특히, JPA 연관 관계와 테스트 코드의 리팩토링에 집중하여 더 나은 품질의 코드를 작성하고, 실무에 적용할 수 있도록 역량을 키워가겠습니다.

백엔드인프런워밍업클럽스터디백엔드프로젝트발자국회고SpringBoot

suover

인프런 워밍업 클럽 스터디 2기 - 백엔드 프로젝트 2주차 발자국

입문자를 위한 Spring Boot with Kotlin - 나만의 포트폴리오 사이트 만들기강의와 함께한 인프런 워밍업 클럽 스터디 2기 - 백엔드 프로젝트 (Kotlin, Spring Boot) 2주차 발자국 입니다. 강의 수강이번 주에는 JPA를 활용해 데이터를 초기화하고, 기본적인 CRUD 작업을 진행하면서 프로젝트의 기초적인 부분을 다졌습니다. 강의를 통해 실습을 진행하며, 코드 작성과 실행 과정을 통해 JPA와 Spring Boot에 익숙해질 수 있었습니다. 특히, 강의를 통해 코드를 작성하는 과정에서 JPA의 기본 사용법을 익히는 데 집중했습니다.주요 학습 내용Spring Data JPA의 기본 CRUD 기능 실습엔티티 간의 기본적인 연관 관계 설정JPA와 데이터베이스 초기화를 통한 실습 환경 설정테스트 코드 작성과 JUnit을 활용한 기본적인 테스트 환경 설정 회고강의와 함께 실습 프로젝트를 진행하면서 JPA와 Spring Boot의 기능을 익혔습니다. 이론 학습보다는 코드 작성에 집중하여, Spring Boot와 JPA의 실질적인 사용법을 몸에 익히는 데 중점을 두었습니다. 실습을 통해 프로젝트 구조와 JPA의 기초 개념을 이해하는 데 도움을 받았습니다.칭찬할 점매일 강의를 듣고 실습 프로젝트를 꾸준히 진행한 점새로운 기술을 익히며 이를 프로젝트에 바로 적용해 본 점실습을 통해 테스트 코드 작성법을 익혀본 점 아쉬웠던 점 및 보완할 점강의를 따라가는 데 집중하다 보니 개념적인 이해가 부족한 부분이 있습니다.실습을 통해 얻은 질문이나 궁금증을 정리해두고, 추가적인 학습을 통해 보완할 계획입니다.미션이번 주 미션은 REST API 설계하기로, 사용자가 게시글과 댓글을 작성하고, 좋아요를 추가하거나 삭제할 수 있는 RESTful API를 설계하는 것이 목표였습니다. 이를 통해 사용자 관리, 게시글과 댓글 관리, 좋아요 기능을 포함한 API를 구축했습니다. 미션 과정API 설계사용자 API: 회원가입, 로그인, 로그아웃, 사용자 정보 조회 및 수정, 비활성화 기능을 설계했습니다.게시글 API: 게시글 작성 및 수정, 삭제 기능과 더불어 게시글 목록 조회 및 특정 게시글 조회를 위한 엔드포인트를 설계했습니다. 또한, 좋아요 추가 및 취소 기능도 포함하여 사용자의 상호작용을 풍부하게 했습니다.댓글 및 답글 API: 댓글 작성, 수정, 삭제 기능과 게시글별 댓글 목록을 조회할 수 있는 기능을 설계했습니다. 댓글에 좋아요를 추가하거나 삭제할 수 있는 기능도 포함하여 사용자가 게시물과 댓글에 대해 보다 활발히 상호작용할 수 있도록 했습니다.  구체적인 API 엔드포인트 및 메서드사용자 API: /api/users 및 /api/users/{userId}를 통해 사용자를 관리할 수 있도록 하였고, 로그인, 로그아웃, 비활성화 기능을 위한 별도의 엔드포인트를 설정했습니다.게시글 API: /api/posts 및 /api/posts/{postId}를 통해 게시글 CRUD와 좋아요 기능을 관리할 수 있도록 설계했습니다.댓글 및 답글 API: /api/posts/{postId}/comments 및 /api/comments/{commentId}로 댓글 CRUD와 좋아요 기능을 관리할 수 있도록 했습니다. 회고이번 API 설계는 RESTful한 접근 방식을 유지하며, 간결하고 직관적인 구조를 갖도록 신경을 썼습니다. 테이블 간의 관계를 고려하여 API를 설계하는 과정에서 실무에서의 데이터 흐름과 REST API의 설계 원칙을 이해하는 데 도움이 되었습니다.아쉬운 점 및 보완할 점아직 JPA 관련 용어와 개념이 생소해서, 이해도가 높아지기까지 시간이 걸렸습니다. 다음 주에는 API 설계를 더욱 구체화하고, 실습하면서 느낀 궁금한 점들을 정리해 해결해 나갈 계획입니다.이번 주는 강의를 통해 프로젝트를 진행하고 API 설계를 하는 경험을 통해 많은 것을 배울 수 있었고, 다음 주에도 꾸준히 학습하며 부족한 부분을 보완해 나가겠습니다.

백엔드인프런워밍업클럽스터디백엔드프로젝트발자국회고SpringBoot

suover

인프런 워밍업 클럽 스터디 2기 - 백엔드 프로젝트 1주차 발자국

입문자를 위한 Spring Boot with Kotlin - 나만의 포트폴리오 사이트 만들기강의와 함께한 인프런 워밍업 클럽 스터디 2기 - 백엔드 프로젝트 (Kotlin, Spring Boot) 1주차 발자국 입니다. 강의 수강이번 주에는 웹 개발의 기본 개념을 학습하며, Spring Boot를 중심으로 한 웹 개발에 대한 전반적인 내용을 다루었습니다. 특히, 웹 서비스의 구성 요소인 클라이언트, 서버, 데이터베이스의 상호작용 방식을 이해하고, 클라이언트와 서버가 데이터를 주고받는 과정에서 발생하는 HTTP 통신과 REST API의 개념을 명확히 정리했습니다.Spring Boot를 활용한 프로젝트 구조와 레이어드 아키텍처(Controller, Service, Repository)를 학습하며, 각 레이어의 역할과 책임에 대해 더욱 깊이 이해할 수 있었습니다. 또한, MVC 패턴과 디자인 패턴의 중요성도 학습하며, 어떻게 하면 코드의 결합도를 낮추고 유지보수성을 높일 수 있는지 고민하는 시간을 가졌습니다. 회고일주일간 강의를 들으면서 웹 개발의 전반적인 흐름과 기본 개념을 더 체계적으로 정리할 수 있었습니다. 이전에도 어느 정도 개념을 알고 있었지만, 이번에는 MVC 패턴이나 레이어드 아키텍처의 필요성에 대해 더 명확히 이해하게 되어 좋았습니다. 프레임워크와 라이브러리의 차이도 명확해져, 앞으로 어떤 상황에서 어떤 도구를 선택할지에 대한 기준이 생긴 것 같아 뿌듯합니다.칭찬할 점매일 꾸준히 강의를 들으며 웹 개발의 기본기를 다진 점학습한 내용을 직접 실습 프로젝트에 적용하면서 이론과 실제를 연결 지은 점이번 주 계획했던 학습 목표를 모두 달성한 점 아쉬웠던 점스프링의 의존성 주입(Dependency Injection)에 대한 이해가 조금 부족하다는 느낌이 있었습니다. 강의에서 개념을 배웠지만, 실제로 이를 프로젝트에 어떻게 적용해야 하는지 고민하는 시간이 좀 더 필요할 것 같습니다. 보완할 점의존성 주입을 주제로 더 깊이 파고들어 실습해보고, 관련된 자료를 찾아보면서 이해도를 높여야겠습니다. 특히, 생성자 주입 방식을 더 명확하게 습득하고 싶습니다. 다음 주 목표다음 주에는 이번 주에 학습한 내용을 바탕으로 더 구체적인 프로젝트를 진행하며, 의존성 주입과 스프링 빈 관리에 대한 이해를 심화할 계획입니다. 이를 통해 Spring Boot의 기능을 제대로 활용할 수 있도록 하겠습니다.미션이번 주 미션은 미니 프로젝트 개발을 주제로, 웹 개발의 기본적인 데이터베이스 테이블을 설계하고 이를 GitHub에 업로드하는 것이 목표였습니다. 프로젝트의 주제는 다양한 활동에서 함께할 사람을 찾고 소통할 수 있는 게시판 서비스로, 사용자, 카테고리, 게시글, 댓글, 그리고 좋아요 기능을 포함한 테이블을 설계했습니다. 미션 과정테이블 설계 사용자 테이블 (users): 사용자의 이메일, 비밀번호, 이름, 닉네임 등을 저장하며, user_role 필드로 사용자 권한을 설정할 수 있습니다.카테고리 테이블 (categories): 게시글의 카테고리를 관리하기 위해 설계된 테이블로, 카테고리 이름을 저장할 수 있습니다.게시글 테이블 (posts): 사용자가 작성한 게시글을 저장하는 테이블로, 사용자와 카테고리와의 관계를 참조하도록 했습니다.댓글 테이블 (comments): 게시글에 달린 댓글을 저장하는 테이블로, 답글까지 고려한 설계를 진행했습니다.좋아요 테이블 (post_likes, comment_likes): 게시글과 댓글에 대한 '좋아요' 기능을 구현하기 위한 테이블을 설계했습니다.  프로젝트 깃허브에 업로드미니 프로젝트를 깃허브에 올리는 작업을 진행했습니다. 이번 작업을 통해 버전 관리와 협업의 중요성을 다시 한번 확인할 수 있었습니다. 회고테이블 설계를 처음부터 끝까지 진행하면서, 데이터베이스 간의 관계를 설계하는 것이 얼마나 중요한지 다시 한번 깨달았습니다. 또한, 테이블 관계를 명확히 이해하지 못했던 부분에서 여러 번 수정을 거치면서 실무적인 감각도 더 키울 수 있었습니다. 특히, 외래 키 제약 조건을 걸어 테이블 간의 참조 무결성을 유지하는 방법을 다시 복습하게 된 것이 큰 수확이었습니다.아쉬운 점테이블 설계를 처음 진행할 때, 부모-자식 관계나 외래 키 제약 조건을 제대로 설정하지 못해 몇 번의 수정을 반복해야 했습니다. 이를 통해 초기 설계의 중요성을 다시 한번 느끼게 되었고, 더 많이 연습해야 한다는 생각이 들었습니다.보완할 점초기 설계 단계에서 더 철저하게 준비하여, 데이터베이스 구조를 명확하게 하고 오류를 최소화하는 방향으로 나아갈 계획입니다.이번 주는 강의와 미션 모두에서 많은 것을 배웠고, 실습을 통해 이론을 실제 프로젝트에 적용하면서 자신감을 얻었습니다. 다음 주에도 꾸준히 학습하며 부족한 부분을 보완해 나갈 계획입니다.

백엔드인프런워밍업클럽스터디백엔드프로젝트발자국회고SpringBoot

leeebug

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

3주차 과제를 일찍 마무리하고 4주차는 조금 일찍 학습을 시작했기 때문인지 유난히도 길었던 마지막 주차도 끝나간다.이번주는 특히나 굉장히 많은 이슈를 경험했다. 결론부터 말하자면 채팅방 생각보다 구현하기 쉽지 않았다. 간단하게 하나만 언급하자면 URL로 장난질 치는 것에 대한 방어로직 구현이 특히 어려웠다. 사실 예전에 firebase 기반으로 미니 SNS를 구현했던 경험이 있는데 이번에 채팅을 구현했으니 이 프로젝트를 그대로 고도화해서 SNS를 완성해볼 계획이다. (실제로 완성할 수 있을지는...)📝 4주차 학습Supabase Auth이메일/비밀번호, OAuth, Magic Link, SMS 인증 등 다양한 인증 방식을 지원하는 인증 서비스supabase.auth.signUp, signInWithOAuth, getUser() 등으로 유저 관리와 세션 제어가 가능JWT 기반으로 RLS와 연동되며, 로그인 상태 자동 유지 및 세션 갱신 기능도 제공Supabase RealtimePostgreSQL의 Listen, Notify 기능을 기반으로 실시간 데이터 동기화 제공테이블 변경(Insert, Update, Delete)을 클라이언트에서 실시간으로 브로드캐스트supabase.chaeenel()로 원하는 이벤트를 구독Supabase RLS(Row Level Security)데이터베이스의 행(Row) 단위로 접근 제어를 설정하는 보안 기능Create Policy를 적용하여 유저별로 조회/ 수정 권한을 세밀하게 조정활성화 시 명시적인 권한 정책 필수아래는 개인적으로 나머지 공부로 학습하고 적용해본 라이브러리입니다.ZodTS 환경에서 런타임 스키마 검증과 타입 추론을 제공하는 유효성 라이브러리z.object() 등 메서드로 구조화된 데이터의 유효성 검사 수행 및 타입 자동 생성서버 및 클라이언트 모두에서 안전한 폼 및 api 검증에 활용React Hook Form과 함께 사용하기 유용한 라이브러리React Tostify토스트 메시지를 손쉽게 띄울 수 있는 라이브러리간단한 API로 다양한 유형의 토스트 알림 제공강력한 커스터마이징 제공Kyfetch API 기반의 모던한 HTTP 클라이언트간결한 문법 제공자동 재시도, 에러 핸들링, 인터셉터 등 확장성과 다수의 편의 기능 포함📋 4주차 미션💬GitHub 저장소👉체험하러 가기 미션 해결 과정 요약이번주 미션의 필수 구현 과제는 Supabas Auth를 사용한 회원가입, 로그인 기능 구현 및 Supabase Realtime을 활용한 1:1 채팅 기능 구현하기였다. 추가 구현 과제는 메시지 삭제, 메시지 알림, 메시지 읽음 여부 표시, 채팅 신고, 유저 차단 기능 등 자유롭게 구현하기였는데 시간 관계 상 전부 구현하긴 어려워서 비교적 쉬운 메시지 삭제와 메시지 알림을 제한적으로 구현했다. 원래는 DB 스키마를 꼼꼼하게 고민하고 시작했어야하는데 급하게 하다보니 처음 계획했던 내용과 많이 달라졌다.myon_users.id -> auth.users.idSupabase Auth로 회원가입된 유저만 등록 가능myon_rooms.userA_id -> myon.users.idmyon_rooms.userB_id -> myon.users.id회원가입된 유저만 채팅에 참여 가능myon_messages.sender_id -> myon_users.id회원가입된 유저만 채팅 전송 가능myon_rooms.last_message_id -> myon_messages.id가장 최근 메시지 미리보기 시 테이블 join에 활용myon_users.username회원가입 시 입력한 닉네임을 기반으로 한글과 특수 문자 등을 제거한 후 중복 발생 시 유틸함수를 통해서 suffix를 불여서 고유한 username을 자동 생성(회원가입 시 입력 폼의 간소화를 위한 선택)✅ 이메일 로그인, 회원가입GET app/auth/signup/callbackPOST api/user/email/register✅ OAuth 로그인, 회원가입GET app/auth/oauth/kakao/callbackPOST api/user/oauth/register우선 회원 기능부터 만들기 시작했는데 강의 패턴을 참고하여 자동 생성되는 auth.users 테이블만 사용하여 회원 로직을 만들었는데 메타 데이터의 형태가 provider 별로 일정하지 않고.. 무엇보다도 auth.users 테이블은 커스텀이 제한적이기 때문에 public.users 테이블을 별도로 관리하였다. 카카오 계정 로그인의 경우 user_metadata를 커스텀 인터페이스로 관리하여 타입 오류를 방지하였다.또한 auth.users 테이블만 단독 사용시의 문제는 회원가입 단계에서 사전에 이메일 중복 검증이 어렵다는 점도 단점이었다.찾아보니 보안상의 이유로 Supabase 내부적으로 auth.users 테이블을 직접 조회하는 기능은 별도로 제공하지 않아서 가입 요청 후에 에러를 캐치할 수 있는 구조이기 때문에 이부분도 public.users를 조회하여 이메일 중복 검증을 통과한 경우에만 회원가입 요청을 할 수 있도록 처리했다.회원가입이나 로그인 인풋 유효성 검증은 Zod + react-hook-form 라이브러리도 대체했다.이메일 회원가입이메일 로그인✅1:1 채팅POST, GET api/rooms/[roomId]POST api/messagesGET api/messages/[roomId]문제는 채팅 기능 구현이었는데 채팅 기능 자체는 Realtime 구독으로 어렵지않게 완성했으나 문제는 방어로직 구현이었다. 몇 가지 예시를 들자면 /direct-message 로 접근 시에 해당 페이지에서 나 자신을 제외한 모든 유저 리스트를 불러온 뒤, /direct-message/:roomId 로 동적 라우트를 구현할 때, 처음에는 고유성을 보장하기 위해서userA_username-userB_username-suffix 형태로 roomId를 생성하는 유틸 함수를 사용했는데 렌더링 시 마다 suffix가 변동되기 때문에 서버단에서 해당 URL이 유효한 URL인지 검증하기가 쉽지 않아서 userA_username과 userB_username을 정렬하여 항상 동일한 roomId를 생성하는 순수 유틸 함수로 변경하여 해당 문제를 해결하였다.export function generateRoomId({ usernameA, usernameB }: { usernameA: string; usernameB: string }) { const sortedUsernames = [usernameA, usernameB].sort() return `${sortedUsernames[0]}-${sortedUsernames[1]}` }과제 추가 구현 기능✅ 메시지 삭제(Soft Delete)PATCH api/message/[messageId]메시지 삭제는 두가지 방식이 있는데 DELETE 메서드를 사용하여 DB Row에서 아예 삭제하는 하드 삭제와 실제 DB Row에서 삭제하지 않지만 is_delete 같은 플래그를 true 하여 클라이언트단에서 감추는 방식인 소프트 삭제 방식이 있다. 하드 삭제의 경우 DB 공간 절약이 필요하거나 탈퇴 회원 정보 등 영구 삭제가 필요한 경우에 적합하고 소프트 삭제의 경우는 복구가 필요하거나 삭제 이력을 추적해야하는 경우에 적합한데 채팅은 로그를 남기는게 중요해서 개인적으로는 소프트 삭제로 구현했다.메시지 호버 시 삭제 아이콘 표시삭제된 메시지✅ 메시지 알림(토스트 메시지 활용)별도 API route 없이 구독으로 구현그냥 마무리하기 아쉬워서 추추가 기능으로 구현했다. 예전부터 토스트 메시지에 관심이 많았는데 직접 구현해보니 생각보다 비효율적이라서 react-tostify 라는 라이브러리를 적용했다.토스트 메시지는 스크롤이 최하단이 아닌 지난 메시지를 읽고 있을때만 우측 상단에 스택 형태로 알림을 보내도록 구현했다. 👀 4주차 회고이번 주는 지난 스터디 기간 동안 진행했던 프로젝트를 배포하는 과정이 포함되어 있었기 때문에, 추가적인 기능보다는 안정적인 배포에 중점을 둘 계획이었으나.. 다행히도 지난주에 미리 매를 맞아두었기 때문에 이번 주 과제 배포는 크게 문제 없이 마무리할 수 있었다.다만 실제 배포 경험이 많지 않다 보니 환경 변수 관련 이슈를 자주 겪게 되었고, OAuth Redirect URL 설정 누락, 빌드 시 타입 오류 등의 경험으로 배포 시 고려해야 할 요소들을 더 잘 이해하게 되었다. 앞으로는 다른 프로젝트 배포 시 참고할 수 있도록 트러블슈팅 내역을 꼼꼼하게 정리하는 습관을 들일 계획이다.이번주에 과제를 진행하면서 딱 한가지 아쉬웠던 점이라면 2주차부터 꾸준히 적용해오던 Container-Presentational Component 패턴을 이번에는 적용시키지 못했다는 점이다. 이번주 과제가 전반적으로 복잡도가 높다보니 관심사 분리를 코드에 녹여내지 못했으나 점진적으로 리팩토링을 통해서 개선해나가기 위해서 백로그에 기록해두었다. 스터디 이후..이번에 구현한 채팅 기능은 실제 배포를 해보니 전송 시 약간의 딜레이가 발생하는 것을 발견했다. 지금 타이밍에서 메시지 전송에 대한 낙관적 업데이트를 적용해서 최적화하는것이 가장 시급한 과제라고 생각한다.끝으로 이번 프로젝트는 이후에 포트폴리오로 활용할 수 있도록 고도화 작업을 이어갈 생각이며, 동시에 타입스크립트에 대한 이해를 더 심화시키고, 쉽진 않겠지만 최근 관심이 생긴 테스트 코드 작성 관련 학습도 병행해나가 보려 한다.정말 마지막으로.. 풀스택 과정을 포함한 모든 3기 스터디 러너분들, 멘토님들과 서포터분들, 워밍업 클럽 관계자 여러분들 모두 고생하셨습니다👏 여러분들 덕분에 좋은 인사이트 얻어갑니다 :)  

풀스택워밍업클럽3기풀스택Next.js4주차회고미션

suover

인프런 워밍업 클럽 스터디 3기 - 백엔드 클린 코드, 테스트 코드 4주차 발자국

Readable Code: 읽기 좋은 코드를 작성하는 사고법Practical Testing: 실용적인 테스트 가이드강의와 함께한 인프런 워밍업 클럽 스터디 3기 - 백엔드 클린 코드, 테스트 코드 (Java, Spring Boot)4주차 발자국 입니다.학습 내용 요약이번 주에는 Practical Testing: 실용적인 테스트 가이드 강의에서 계층형 아키텍처(Layered Architecture) 의 개념을 다시 한번 정리하고, 스프링 기반 테스트에서 자주 사용하는 Mock/Spy 기법과 관련 애너테이션들을 배웠습니다.Layered Architecture (계층형 아키텍처)Presentation Layer: 사용자 입력과 응답을 담당. API 요청/응답 혹은 프론트엔드 인터페이스를 제공하며, 기본적인 파라미터 검증과 변환만 수행.Business Logic Layer: 애플리케이션의 핵심 비즈니스 로직과 규칙을 구현. 서비스(Service)나 도메인(Domain) 로직이 위치하며, 트랜잭션 관리의 주된 대상이 됨.Persistence Layer: 데이터베이스/파일시스템 등 영속성 저장소와 직접적으로 상호작용. Repository(DAO) 형태로 CRUD를 담당.Mockito & Spring Test 관련 애너테이션@Mock / @Spy / @InjectMocks: 순수 Mockito 환경(스프링 컨텍스트 없이)에서 가짜 객체(또는 부분 가짜 객체)를 만들어 단위 테스트를 쉽게 작성할 수 있게 함.@MockBean / @SpyBean: 스프링 애플리케이션 컨텍스트를 기동하는 통합 테스트 환경에서 특정 Bean을 Mock 또는 Spy로 교체하여, 외부 의존성을 간단히 제어하고자 할 때 활용.테스트 설계 방향각 레이어별로 테스트 범위를 명확히 하여, “Persistence → Business → Presentation” 순으로 책임을 분리해 점진적으로 테스트 커버리지를 넓힘.@MockBean / @SpyBean을 과도하게 사용하지 않도록 주의: 꼭 필요한 부분만 모의(Mock/Spy) 처리하고, 나머지는 실제 로직을 이용해 통합 테스트를 안정적으로 수행.테스트 시나리오별로 @BeforeEach를 활용하는 방법과, 각 테스트 메서드에서 독립적으로 given-when-then을 구성하는 방법을 비교.학습 회고얻은 인사이트레이어별 책임과 역할이 분명해야 복잡한 서비스에서도 테스트 설계가 한결 명확해짐을 느꼈습니다.Mock/Spy를 올바른 상황에서만 사용하면 외부 의존성을 최소화하여 빠른 단위 테스트를 작성할 수 있으나, 반대로 불필요하게 남발하면 테스트 유지보수가 어려워짐을 체감했습니다.어려웠던 점@MockBean과 @SpyBean을 사용해 스프링 컨텍스트를 띄울 때 테스트 속도가 느려지거나, 동일 타입 Bean이 여러 개일 때 어느 Bean을 대체하는지 혼동될 수 있었습니다.레이어별 테스트를 작성하면서, 중복으로 보이는 코드(예: 테스트 준비 로직)를 어느 정도까지 @BeforeEach로 추출해야 할지 고민이 되었습니다.미션🎯 Day16 미션: Layered Architecture 구조의 레이어별 특징과 테스트 방법요구사항Layered Architecture에서 각 레이어가 하는 역할, 특징, 그리고 테스트 방식을 자기만의 언어로 정리하기나만의 정리Presentation Layer역할: 사용자나 외부 시스템의 요청을 받고 응답을 전달. 파라미터 검증, DTO 변환, HTTP 상태 코드 결정 등을 담당.특징: 비즈니스 로직을 직접 수행하지 않고, 유효성 체크 후 Service 호출 → 결과 반환에 집중.테스트 방법: 컨트롤러 통합 테스트(@SpringBootTest, @WebMvcTest + MockMvc)를 통해 실제 요청/응답 형식을 모의하거나, 프론트엔드와의 e2e 테스트를 진행해볼 수도 있음.Business Logic Layer역할: 애플리케이션의 핵심 규칙, 알고리즘, 트랜잭션 등의 로직 담당.특징: 여러 Repository를 조합하여 도메인 로직을 수행하고, 예외 처리, Validation 등의 업무 로직을 포괄적으로 관리.테스트 방법: 단위 테스트를 통해 특정 비즈니스 로직의 정확성을 검증. 필요 시 Repository를 Mock 처리(예: @Mock, @MockBean)하여 DB 의존성을 제거하고 로직만 집중 테스트.Persistence Layer역할: 데이터의 저장/조회/수정/삭제 등 영속성 처리에 집중.특징: 쿼리 작성, DB 연결, CRUD 로직을 추상화한 Repository/DAO 형태로 제공.테스트 방법: 실제 DB 혹은 In-Memory DB(H2 등)를 활용한 통합 테스트(@DataJpaTest). 쿼리 정확도, 트랜잭션 처리, 성능 등을 검증하기에 좋음.🎯 Day18 미션 1: @Mock, @MockBean, @Spy, @SpyBean, @InjectMocks의 차이점 정리요구사항각 애너테이션이 어떤 환경(단위 테스트/통합 테스트)에서 사용되고, 어떤 특징이 있는지 정리하기1) @Mock주로 사용 환경: Mockito (단위 테스트)스프링 빈 등록 여부: X (별도 Bean 아님)특징:순수 Mock 객체를 생성하여, 내부 로직 없이 호출 기록/결과(Stubbing)만 지정할 수 있음단위 테스트에서 외부 의존성을 배제하고 특정 로직만 검증할 때 유용2) @MockBean주로 사용 환경: Spring Boot Test(통합 테스트)스프링 빈 등록 여부: O (빈으로 등록됨)특징:스프링 애플리케이션 컨텍스트에 등록된 실제 Bean을 Mock으로 교체다른 Bean이 해당 Bean을 의존하고 있으면, 그 의존성도 가짜(Mock)로 처리됨@SpringBootTest나 @WebMvcTest 등 스프링 컨텍스트가 뜨는 환경에서 사용3) @Spy주로 사용 환경: Mockito (단위 테스트)스프링 빈 등록 여부: X (별도 Bean 아님)특징:부분 가짜(Spy) 객체를 생성기본적으로 실제 메서드 동작을 유지하면서, 필요한 부분만 가짜(Stubbing)로 설정 가능복잡한 객체를 테스트할 때, 일부는 실제 로직을 실행하고 일부만 Mock 처리할 수 있어 유연함4) @SpyBean주로 사용 환경: Spring Boot Test(통합 테스트)스프링 빈 등록 여부: O (빈으로 등록됨)특징:스프링 컨텍스트 내 실제 Bean을 Spy로 교체기본적으로는 실제 로직을 수행하되, 특정 메서드만 가짜로 동작시키거나 호출을 기록할 수 있음통합 테스트 환경에서 부분 Mocking이 필요할 때 적합5) @InjectMocks주로 사용 환경: Mockito (단위 테스트)스프링 빈 등록 여부: 해당 없음특징:@Mock 또는 @Spy로 만들어진 객체들을 자동으로 주입(생성자, 세터, 필드 순)해줌예: @InjectMocks UserService라면, UserService가 의존하는 Repository나 다른 객체들을 @Mock으로 생성해 주입받을 수 있음단위 테스트에서 여러 의존 객체를 편리하게 Mock/Spy로 대체할 수 있도록 지원 🎯 Day18 미션 2: 테스트 시나리오(@BeforeEach, given, when, then) 구성요구사항아래 3개의 테스트 메서드가 있을 때, “어떤 준비 로직을 @BeforeEach로 뽑고, 어떤 부분을 각 테스트의 given절에 둘지”, “when절을 어떻게 구성할지”를 구상해보기예시 테스트 3종사용자가 댓글을 작성할 수 있다.사용자가 댓글을 수정할 수 있다.자신이 작성한 댓글이 아니면 수정할 수 없다.나의 구성@BeforeEach void init() { // 공통 데이터를 미리 생성하지 않습니다. /* * 각 테스트가 각각의 상황(사용자, 게시물, 댓글)을 자유롭게 구성할 수 있도록, * 여기서는 사전에 아무것도 세팅해두지 않습니다. * 테스트마다 필요한 데이터 타입이나 조건이 다를 수 있으므로, * 각 테스트 메서드 내부에서 직접 객체를 생성하고 준비해 주는 방식을 채택했습니다. */ } @DisplayName("사용자가 댓글을 작성할 수 있다.") @Test void writeComment() { // given // 1-1. 사용자 생성에 필요한 내용 준비 // 1-2. 사용자 생성 // 1-3. 게시물 생성에 필요한 내용 준비 // 1-4. 게시물 생성 // 1-5. 댓글 생성에 필요한 내용 준비 // when // 1-6. 댓글 생성 // then // 검증 } @DisplayName("사용자가 댓글을 수정할 수 있다.") @Test void updateComment() { // given // 2-1. 사용자 생성에 필요한 내용 준비 // 2-2. 사용자 생성 // 2-3. 게시물 생성에 필요한 내용 준비 // 2-4. 게시물 생성 // 2-5. 댓글 생성에 필요한 내용 준비 // 2-6. 댓글 생성 // when // 2-7. 댓글 수정 // then // 검증 } @DisplayName("자신이 작성한 댓글이 아니면 수정할 수 없다.") @Test void cannotUpdateCommentWhenUserIsNotWriter() { // given // 3-1. 사용자1 생성에 필요한 내용 준비 // 3-2. 사용자1 생성 // 3-3. 사용자2 생성에 필요한 내용 준비 // 3-4. 사용자2 생성 // 3-5. 사용자1의 게시물 생성에 필요한 내용 준비 // 3-6. 사용자1의 게시물 생성 // 3-7. 사용자1의 댓글 생성에 필요한 내용 준비 // 3-8. 사용자1의 댓글 생성 // when // 3-9. 사용자2가 사용자1의 댓글 수정 시도 // then // 검증 }@BeforeEach정말 모든 테스트에 공통으로 필요한 세팅(예: DB clean-up, 동일한 테스트 데이터 세팅 등)이면 이곳에 배치.하지만 테스트마다 시나리오가 크게 다른 경우, 오히려 각 테스트 내부(given 절)에서 데이터를 생성하는 편이 가독성과 유연성이 좋아질 수 있음.given 절테스트에 필요한 사전 조건(사용자, 게시물, 댓글 등) 및 Mock/Stubbing이 필요하다면, 해당 로직을 배치.시나리오별로 조금씩 다른 상황을 구성할 때, @BeforeEach보다 각 테스트 내부의 given 부분에 명시적으로 작성하는 방식이 선호됨.when 절실제 테스트 대상 메서드를 호출하는 구간. 한 개의 테스트에서 “정확히 하나의 when 절”을 유지해, 명확히 어떤 동작을 검증하는지 드러내도록 함.then 절결과 검증(Assertions) 수행. 저장된 데이터 확인, 예외 발생 여부 확인, 반환값 확인 등.미션 회고레이어별 특징을 자기 언어로 설명단순히 문서상 개념을 반복하기보다, 실제 현업/프로젝트에서 마주치는 구조를 떠올리며 다시 풀어내보니, 각 계층이 왜 필요한지, 테스트를 어떻게 접근해야 하는지 더 명확해졌습니다.Mock/Spy 애너테이션 정리정리를 해보니 실제로 코드에 적용할 때 어떤 상황에서 무엇을 써야 하는지가 더 분명해졌습니다.@MockBean/@SpyBean이 스프링 컨텍스트를 사용하는 통합 테스트에서 유용하지만, 속도와 Bean 교체 이슈가 있어 주의가 필요하다는 점이 인상적이었습니다.3가지 테스트 시나리오에서 @BeforeEach & given/when/then 배치시나리오에 따라 @BeforeEach를 최소화하거나, 여러 테스트에 공통적으로 필요한 부분만 추출하는 방식이 각각 장단점이 있음을 확인했습니다.테스트 구조를 “given-when-then”으로 고정하면 가독성이 올라가지만, 너무 세세한 단계 분리는 오히려 장황해질 수 있으므로 균형을 잡아야겠습니다.회고칭찬하고 싶은 점레이어별 테스트 기법과 Mock/Spy 전략이 한층 체계적으로 잡혔습니다.실제 코드를 작성하며, “가짜 객체를 어디까지 써야 하나? 외부 연동은 어떻게 테스트하나?” 같은 고민을 많이 해 봐서, 앞으로는 목적에 맞는 테스트를 설계하는 능력이 향상될 것 같습니다.아쉬웠던 점 & 보완 계획Mock 남발 시 발생할 수 있는 문제(설정 복잡도 상승, 실제 로직 변경 시 Stubbing 깨짐)를 더 구체적인 예제로 다뤄보면 좋을 것 같습니다.@BeforeEach와 개별 테스트 내부 given 절의 적절한 균형점을 찾으려면, 실제 현업 수준의 다양한 테스트 케이스를 더 경험해 봐야겠습니다.다음 목표예외 상황 테스트 강화단순 성공 케이스뿐 아니라, 비정상 입력이나 예외 케이스를 좀 더 체계적으로 정리하고 테스트에 반영. 도메인 별로 테스트 슬라이싱@DataJpaTest, @WebMvcTest 등 스프링이 제공하는 슬라이스 테스트 방식을 적극 활용해 보기.테스트 실행 속도 최적화통합 테스트와 단위 테스트를 적절히 조합하여, 빠르면서도 신뢰성 있는 테스트 환경을 구축해 보기.이번 4주차에는 계층형 아키텍처를 다시 한번 복습하면서 Mock/Spy, @MockBean/@SpyBean 등 스프링 테스트 환경에서 자주 쓰이는 기법들을 정리하고 적용해 보았습니다. 학습 내용과 미션을 통해 각 레이어가 가진 의미와 책임이 더욱 또렷해졌고, 가짜 객체를 어떻게 잘 활용해야 하는지 감이 잡힌 것 같습니다.앞으로도 학습 내용을 기록하고 회고하면서, 점점 더 탄탄한 테스트 코드를 작성해 가도록 하겠습니다.감사합니다!

백엔드인프런워밍업클럽스터디백엔드클린테스트코드발자국회고3기

보키

[인프런 워밍업 클럽 3기 - BE/Project] 3주차 회고 발자국 🐾

2주차와 마찬가지로 KPT 회고 프레임워크를 선택해서 작성해보려고 한다! Keep(만족, 지속하고 싶은 부분)이번 3주차는 개인프로젝트에서는 프로젝트쪽에서는 조회 API를 만들었다. API 테스트에서는 mockMVC를 사용하지 않고, 실제 인증 엔드포인트를 호출하고, AccessToken을 받아서 Authorization 헤더에 넣어서 Project 목록을 가져오는 것을 RestClient로 작성했다.DB url은 src단과 test단에서 다른 곳을 바라보게 만들었다. 개인적으로는 컨트롤러->서비스->레포지토리->서비스->컨트롤러 로 흐르는 스택트레이스 요청&응답을 제대로 검증하기위해서는 실제로 사용하는 DB 벤더까지 맞춰야 한다고 생각한다.실제로 배포했다면 AWS 인스턴스로 요청을 날려보면 되겠고! 갠적으로 h2는 학습용으로는 괜찮지만 실제 프로젝트를 할때는 테스트에도 비슷하거나 같은 환경으로 해야한다고 생각한다.강의에서는 ADMIN단을 진행했다. 거의 끝나가고있다!! 좋아! Problem(부족, 아쉬웠던 부분)사실, 이번주에 면접이 있었고, 면접준비도 하느라고 조회과제 테스트코드랑, 금요일까지였던 미션을 완료 못했다.늦게라도 제출을 해서 마무리를 하는 멋쥔 사람이 되려고 노력할 것이다!Try(도전, P에 대한 해결책 등으로 다음번에 보완하거나 시도할 부분)거의 다 온거, 내 프로젝트도 천천히 그리고 차근차근 보완해가며 완성시켜보고 싶다.프론트엔드도 예전에 학습한거를 바탕으로 현업에서 썼던 Vue3대신 React(CRA)로 붙여보고 싶다.테스트코드도 한번 작성해놓으니까 Fixture-Monkey나 Kotest, mockk 사용법이 새록새록 기억도 나고 새로 배우는 것도 있고 그렇다.앞으로 쭉 잘하면 된다!! 나 자신을 응원하자!!!

웹 개발인프런워밍업클럽회고KPT3주차백엔드프로젝트KotlinSpringboot

suover

인프런 워밍업 클럽 스터디 3기 - 백엔드 클린 코드, 테스트 코드 3주차 발자국

Readable Code: 읽기 좋은 코드를 작성하는 사고법Practical Testing: 실용적인 테스트 가이드강의와 함께한 인프런 워밍업 클럽 스터디 3기 - 백엔드 클린 코드, 테스트 코드 (Java, Spring Boot)3주차 발자국 입니다.학습 내용 요약이번 주에는 Spring & JPA 환경에 계층형 아키텍처(Layered Architecture) 및 테스트 기법(통합 테스트, MockMvc, JPA 테스트 등)을 학습했습니다.1⃣ Spring & JPASpring이 제공하는 IoC/DI 개념과 ORM(JPA, Hibernate) 기반 데이터 접근 방식을 이해했습니다.엔티티(Entity) 설계를 통해 도메인 모델링을 하고, 리포지토리(Repository) 계층으로 데이터 CRUD를 담당하도록 분리했습니다. 2⃣ Layered ArchitecturePresentation Layer(Controller)Business Layer(Service)Persistence Layer(Repository)컨트롤러는 요청/응답을 처리하고, 비즈니스 로직은 서비스가 담당하며, 데이터 접근은 리포지토리를 통해 수행합니다.3⃣ 테스트 코드 작성단위 테스트: 특정 클래스나 메서드 단위 검증. 예: JUnit + AssertJ 사용통합 테스트: 스프링 컨텍스트를 로드해 여러 레이어(Controller, Service, Repository)의 협력을 검증.MockMvc를 사용해 테스트하는 방식을 배웠습니다.4⃣ 유효성 검증(Bean Validation)DTO에서 @NotEmpty, @NotNull, @Positive 등 애노테이션을 사용하여 입력값을 검증하고,RestControllerAdvice로 BindException 등을 처리하여 예외 상황 시 API 응답을 표준화했습니다.✨ 학습 회고Spring & JPA 학습을 통해, 계층형 아키텍처 전체 흐름을 이해해야 함을 느꼈습니다.테스트 코드에서, 단위 테스트와 통합 테스트의 경계와 필요성을 체감했습니다.JUnit5 + AssertJ로 간단한 메서드 단위 검증을 할 때는 편리했지만, MockMvc로 검증하거나, @DataJpaTest로 테스트할 때는 더 많은 설정이 필요했습니다. 그만큼 테스트 커버리지가 넓어지며 신뢰도가 높아진다는 장점을 느꼈습니다.Validation과 ExceptionHandler를 통해 잘못된 입력을 막고, 표준화된 에러 응답을 제공하는 프로세스가 얼마나 중요한지 깨달았습니다.미션 해결 과정🎯 스터디카페 이용권 선택 시스템 테스트 미션미션스터디카페 이용권 선택 시스템의 테스트 코드를 작성하는 미션.미션 목표메시지 출력 테스트할인 로직 테스트사물함 선택 테스트 해결 과정OutputHandlerTest환영 메시지 + 공지사항 “프리미엄 스터디카페” 등의 문구가 제대로 찍히는지 확인.주문 내역 요약에 “이용 내역”, “이용권”, “사물함”, “총 결제 금액” 같은 키워드가 포함되는지 검증.StudyCafePassOrderTest할인율 및 가격 계산이 의도대로 동작하는지 (예: (250,000 + 10,000) - 할인액 25,000 = 235,000) 확인.사물함이 있는지 없는지에 따라 Optional<LockerPass>가 올바르게 반환되는지 테스트.StudyCafeSeatPassTest할인율이 0일 때 할인 금액이 0원인지, 0.1이면 정상 할인액이 계산되는지.시간권(HOURLY)은 cannotUseLocker() == true, 고정석(FIXED)은 false인지 등을 테스트로 보장. 회고도메인 로직이 잘 분리되어 있어 테스트 작성이 수월했습니다. 만약 복잡한 코드가 여기저기 있었다면, 테스트도 훨씬 어렵고 중복될 뻔했습니다. 또한 예외 처리 부분에 대한 테스트 코드의 필요성도 느껴졌습니다. 예외 상황에 대한 흐름을 명확하게 검증하고, 안정적인 동작을 보장하기 위해서는 예외 처리에 대한 테스트도 함께 구성되어야 한다고 생각했습니다.🔗 테스트 미션 깃허브 링크회고스스로 칭찬하고 싶은 점도메인 흐름 파악: Controller → Service → Repository 순으로 데이터가 흐르는 구조를 더욱 제대로 이해하게 되었습니다. 다양한 스프링 테스트 어노테이션(WebMvcTest, SpringBootTest, DataJpaTest 등)을 직접 적용하며, 각 용도에 맞게 테스트를 구분해본 경험이 유익했습니다.아쉬웠던 점 & 보완할 점테스트 격리: 통합 테스트 시 상태를 초기화하는 과정이 번거로웠고, 테스트 순서에 따라 의존성이 생기지 않도록 더 철저히 관리해야 함을 느꼈습니다.테스트 시나리오 다양성 부족: 대부분 해피 케이스 위주로 검증한 감이 있어, 더욱 폭넓은 예외 상황에 대한 시나리오를 추가 작성하면 안정성이 높아질 것 같습니다.다음 주 학습 목표Mock과 Test Double: Mock과Test Double 개념을 학습하고, 적절히 사용해 볼 예정입니다.테스트 환경 독립성:테스트 시 독립적인 환경을 유지하는 방법을 살펴볼 예정입니다.테스트 개선 기법:조금 더 간결하고 가독성 높은 테스트 작성을 시도해 볼 계획입니다.이번 주에는 Spring & JPA 환경에서의 테스트 코드 작성을 중점적으로 경험 했습니다. 이를 통해 서비스가 단계적으로 확장되더라도, 테스트 코드가 안정적인 작동을 보장할 수 있다는 점을 다시금 깨달았습니다. 앞으로도 테스트 우선 방식으로 새로운 기능이나 리팩토링을 진행해, 안전하고 효율적인 협업 환경을 유지해 나가겠습니다.감사합니다!

백엔드인프런워밍업클럽스터디백엔드클린테스트코드발자국회고3기

leeebug

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

워밍업 클럽도 벌써 3주차!처음 스터디를 시작할 때와 비교하면 가장 큰 소득은 React Query에 익숙해졌다는 것 그리고 무언가에 몰입하면서 성취감을 느꼈다는 것이다.단순히 기능을 구현하는 걸 넘어서 최적화나 에러 핸들링까지 고민하는 과정이 꽤 재밌었다.이번주에는 인피니트 쿼리와 추가 기능으로 좋아요 기능을 구현해야해서 지난주와 마찬가지로 조금 일찍 학습을 시작했다.깃 레포는 역시 첫번째 과제에 사용했던 템플릿을 거의 수정없이 그대로 사용해서 역시나 개발환경 구축은 무리없이 진행했다.다만, 한 가지 아쉬운 점이라면 터보레포 같은 모노레포 도구를 도입했어야 하지 않았나 하는 생각이 들었다는 점이다.현재 방식은 각 주차별 과제를 독립적인 레포로 관리하고 있는데, 공통 유틸이나 자주 사용하는 설정 파일을 계속 복붙하는 과정에서 의외로 피로감을 느꼈다.우선 마지막 과제까진 지금의 방식을 유지하고 스터디 마무리 이후에 터보 레포에 대해서는 개별적으로 학습을 해볼 예정이다.📝 3주차 학습useInfiniteQuery무한 스크롤 및 페이지네이션을 위한 React Query 훅fetchNextPage를 사용해 추가 데이터 요청getNextPageParams로 다음 페이지 여부 관리 useInViewreact-intersection-observer 라이브러리의 훅특정 요소가 화면에 보이는지 감지(뷰포트 진입 여부로 확인)무한 스크롤 구현 시 useInfiniteQuery와 함께 사용threshold, rootMargin으로 감지 범위 조절 가능📋 3주차 미션💬 GitHub 저장소🚀 데모 영상 보러가기미션 해결 과정 요약이번주 미션의 필수 구현 과제는 무한 스크롤과 SEO 추가, 영화 검색 기능 구현하기였다. 추가 기능으로 영화 좋아요 기능을 구현했는데, 예전에 SNS를 만들때 경험해봤던 기능이라 쉽게 구현할 수 있을 것이라 기대했다. 하지만 예상과는 달리, 유저 식별 기능이 없다는 점이 문제였다. SNS 좋아요 기능 구현 당시에는 사용자 ID 기반으로 좋아요를 관리했지만 이번 프로젝트는 익명 유저 환경이라 데이터를 어떻게 저장하고 관리할지 고민이 필요했다.처음에는 movies, users, liked_movies 3개의 테이블을 생성하여 user_id, movies_id를 복합키로 설정해 브라우저별 익명 유저를 관리하는 방식을 시도했으나 구현 복잡도가 너무 높아지는 문제로 단순화하는 방식으로 변경했다.movies 단일 테이블에 like_count 필드를 추가하고 브라우저별로 좋아요 상태를 관리하는 방식으로 해당 기능을 구현했다. 이 방식의 단점은 브라우저 변경 시 개인별 좋아요 리스트를 추적할 수 없다는 점이지만 애초에 유저 식별 기능을 배제한 상황에서 선택할 수 있는 최적의 방식이라고 판단하여 적용했다.그리고 강의에서는 movies.id를 auto increment id로 구현했지만 더 나은 확장성을 위해서 uuid를 고려했다. 다만 uuid는 URL에서 사용하기 불편하여 가독성이 좋은 slug 칼럼을 별도로 추가하였다. API 요청 파라미터를 id에서 slug로 대체하면서 가독성과 SEO 최적화까지 함께 챙겨갈 수 있었다.slug Column 추가ALTER TABLE myreel_movies ADD COLUMN slug TEXT UNIQUE;중복되는 Row 제거 (제공되는 DB에 중복되는 데이터가 9건 발견되었다.)DELETE FROM myreel_movies WHERE id NOT IN ( SELECT id FROM ( SELECT id, title, order_index, ROW_NUMBER() OVER (PARTITION BY title ORDER BY order_index ASC) AS row_num FROM myreel_movies ) ranked WHERE row_num = 1 );영화 title 기준으로 slug 생성예시 - 'Dune: Part Two' -> 'dune-part-two'UPDATE movies SET slug = LOWER(REGEXP_REPLACE(title, '[^a-zA-Z0-9]+', '-', 'g')) WHERE slug IS NULL;과제 추가 구현 기능✅ 영화 좋아요 추가api/movies/:slug/likeconst likeMovie = async () => { try { const res = await fetch(`${baseUrl}${API_ENDPOINTS.LIKE(slug)}`, { method: 'POST', }) if (!res.ok) { throw new Error(CLIENT_ERROR.MOVIE_LIKE_FAILED.message) } const data: LikeMovieResponseDTO = await res.json() setLikeCount(data.like_count) // 서버에서 받아온 새로운 좋아요 수로 업데이트 setIsLiked(true) // 로컬 스토리지에 영화 추가 또는 업데이트 const likedMovies: LikedMovie[] = JSON.parse(localStorage.getItem('likedMovies') || '[]') // 이미 좋아요를 누른 영화가 있다면, likeCount를 업데이트 const existingMovieIndex = likedMovies.findIndex((movie) => movie.slug === slug) if (existingMovieIndex >= 0) { likedMovies[existingMovieIndex].likeCount = data.like_count // 좋아요 수 업데이트 } else { // 좋아요를 누른 적이 없다면 새로 추가 const newLikedMovie = { slug, likeCount: data.like_count } likedMovies.push(newLikedMovie) } localStorage.setItem('likedMovies', JSON.stringify(likedMovies)) } catch (error) { console.error(error) } }✅ 영화 좋아요 삭제api/movies/:slug/unlikeconst unlikeMovie = async () => { try { const res = await fetch(`${baseUrl}${API_ENDPOINTS.UNLIKE(slug)}`, { method: 'POST', }) if (!res.ok) { throw new Error(CLIENT_ERROR.MOVIE_UNLIKE_FAILED.message) } const data: LikeMovieResponseDTO = await res.json() setLikeCount(data.like_count) setIsLiked(false) // 로컬 스토리지에서 해당 영화 정보 삭제 const likedMovies: LikedMovie[] = JSON.parse(localStorage.getItem('likedMovies') || '[]') const updatedLikedMovies = likedMovies.filter((movie) => movie.slug !== slug) // 로컬 스토리지 갱신 localStorage.setItem('likedMovies', JSON.stringify(updatedLikedMovies)) } catch (error) { console.error(error) } }개인 챌린지 기능✅ 메인 페이지 최상단으로 가는 버튼 추가메인 페이지에서 500px 이상 스크롤 내릴 경우 최상단으로 이동하는 버튼 생성behavior: 'smooth' 로 부드럽게 이동 ✅ 검색 결과 없을 경우, 좋아요 많은 순 추천 영화 6개 노출되는 기능 구현좋아요가 많은 영화 외에도 최근 개봉한 영화 같은 다양한 리스트 제공 예정api/movies/most-liked👀 3주차 회고지난주에 적용했던 매니져 컴포넌트 / UI 컴포넌트로 분리하는 방식이 Container-Presentational Component 패턴 과 유사한 방식이라는 것을 다른 러너분의 발자국을 통해 알게되었다. 궁금해서 조금 더 찾아보니, 이 패턴은 과거 클래스형 컴포넌트 시절에는 HOC(High Order Component)와 함께 많이 사용되었지만, 함수형 컴포넌트에서도 여전히 유효한 방식이라는 것을 알게되었다. 이번주에는 기존 패턴을 유지하면서도, 비즈니스 로직을 최대한 커스텀 훅으로 분리하는 연습을 진행했다. 이를 통해 컴포넌트의 역할을 더욱 명확하게 나누고, 재사용성과 유지보수성을 높이는 방향으로 조금씩 개선되고 있다는 것을 체감했다.👻 배포 관련 이슈 (3월 22일 추가)4주차에 스터디 기간 개발한 4개의 프로젝트를 모두 배포하는것이 기존 스터디 일정이지만.. 시간적 여유가 생겨서 1~3주차 프로젝트를 미리 배포해봤다. vercel은 기존에 사용하던 툴이었는데 한번에 3개의 프로젝트를 배포하려고 시도하는 과정에서 수 많은 에러를 경험했다. 4주차 프로젝트 배포시, 추후 다른 프로젝트 배포시에 참고할 수 있도록 간단하게 정리해본다. ✅ @/components/... 앨리어스 관련 캐싱 이슈문제 개발 환경에서는 정상 작동하던 import가 Vercel 배포 시에만 Module not found 에러 발생원인Vercel의 캐싱 문제 또는 파일명 인식 관련 문제(대소문자, 내부 경로 변경 후 캐시 꼬임)해결@ 앨리어스 문제를 의심하여 상대 경로로 변경 후 재배포 시도 -> 해결 안됨컴포넌트 경로의 대소문자 확인후 재배포 시도 -> 해결 안됨 Title.tsx 파일명을 AppTitle.tsx로 변경하여 강제로 캐시 무력화 후 재배포 시도 -> 해결 ✅ params 비동기 처리 관련 타입 에러 (Next.js 15)문제page.tsx에서 params를 비동기적으로 처리하려 하자, params 타입이 Promise로 인식되어 타입 오류 발생원인 (깃헙 이슈 참고)Next.js 15 내부적으로 PageProps가 비동기적 처리를 기대하거나 타입 추론이 변경됨params 타입이 Promise<any>로 추론되어 관련 에러 발생PageParams 제네릭 타입 해석 충돌next dev에서는 정상 작동하지만 next build 시 오류 발생해결params의 인터페이스를 명시적 타이핑 -> 해결 안됨params의 타입 any로 명시하고 타입 단언으로 처리 -> 해결 안됨배포 시 안정성 확보를 위해서 Next.js 14 + React 18 버전으로 롤백 -> 해결✅ Tailwind CSS 적용 안됨문제배포된 페이지에서 Tailwindcss 클래스가 적용되지 않음원인Next.js 15 -> Next.js 14, React 19 -> React 18로 롤백하는 과정에서 관련된 의존성 충돌이 일어난것으로 예상됨해결Tailwindcss, postcss, autoprefixer 의존성 삭제 후 캐시 초기화 후 재설치 -> 해결 ✅ 환경 변수(NEXT_PUBLIC_BASE_URL) 미설정으로 fetch 실패문제빌드 시 fetch 요청이 localhost:3000으로 날아가면서 ECONNREFUSED 에러 발생원인Vercel 환경 변수 설정 시 NEXT_PUBLIC_BASE_URL 값을 localhost:3000으로 설정하여 에러 발생해결해당 환경 변수를 실제 배포 URL로 변경 후 재배포 시도 -> 해결

풀스택워밍업클럽3기회고발자국3주차

suover

인프런 워밍업 클럽 스터디 3기 - 백엔드 클린 코드, 테스트 코드 2주차 발자국

Readable Code: 읽기 좋은 코드를 작성하는 사고법Practical Testing: 실용적인 테스트 가이드강의와 함께한 인프런 워밍업 클럽 스터디 3기 - 백엔드 클린 코드, 테스트 코드 (Java, Spring Boot)2주차 발자국 입니다.학습 내용 요약이번 주는 클린 코드 작성법에 이어 코드 구조와 리팩토링, 그리고 테스트 코드 작성법을 본격적으로 학습했습니다.1⃣ 주석의 양면성주석은 코드 자체로 표현하기 어려운 의사결정의 히스토리를 기록할 때 사용해야 합니다.자주 변하는 정보는 주석으로 표현하면 안 되고, 주석은 코드와 함께 꾸준히 관리해야 합니다.부정확한 주석은 주석이 없는 코드보다 더 해롭습니다.2⃣ 변수와 메서드의 나열 순서변수는 사용하는 순서대로 나열하고, 공개 메서드를 먼저 배치하여 외부 세계에 제공하는 기능을 명확히 드러냅니다.비공개 메서드는 공개 메서드에서 호출되는 순서로 나열하여 코드 읽기 흐름을 자연스럽게 만듭니다.나열 순서도 의도를 표현하는 중요한 수단이 될 수 있음을 배웠습니다.3⃣ 패키지 나누기패키지 구조는 도메인 문맥을 반영하여 설계하며, 너무 잘게 나누거나 너무 크게 뭉치는 것을 지양해야 합니다.팀 내 합의가 중요하며 초기 설계 단계에서 패키지 구조를 충분히 고민해야 합니다.4⃣ 적정 기술의 사용모든 기술은 적정한 수준과 시점에서 활용해야 하며, 무리한 추상화나 오버 엔지니어링은 오히려 개발 효율성을 떨어뜨립니다.클린 코드는 만능 해결책이 아니며, 상황과 목적에 맞는 균형 잡힌 접근이 중요합니다.5⃣ 테스트 코드와 TDD자동화된 단위 테스트(JUnit5, AssertJ)를 통해 신뢰성과 유지보수성을 높이고, 빠른 피드백을 받을 수 있습니다.TDD(Test Driven Development)를 통한 선(先) 테스트 작성, 후(後) 기능 구현 방식을 이해했습니다.Given-When-Then 구조를 이용한 BDD(Behavior Driven Development) 스타일로 작성하면 도메인 로직을 명확히 표현할 수 있습니다.미션 해결 과정🎯 스터디카페 이용권 선택 시스템 리팩토링 미션미션'스터디 카페 이용권 선택 시스템'의 기존 시스템에 중복된 로직과 혼재된 책임을 명확히 분리하고, 객체지향 원칙을 준수하여 코드 구조를 개선하는 리팩토링 미션입니다.접근 관점추상화 레벨 맞추기: 중복 로직을 별도 메서드로 추출했습니다.객체의 책임 분리: StudyCafeOrder 객체를 추가해 할인 계산 로직을 전담했습니다.DIP(의존성 역전 원칙): 파일 접근 로직을 인터페이스(StudyCafeRepository)로 추상화하여 확장성을 확보했습니다.해결 과정1. 기존의 복잡한 분기 조건(if-else)을 메서드로 깔끔하게 정리했습니다.Before: if (HOURLY) { ... } else if (WEEKLY) { ... } else if (FIXED) { ... } 로직이 길고, 사물함 로직도 그 안에서 처리.After:public void run() { // 1) passType 선택 (시간권/주단위/고정석) StudyCafePassType passType = inputHandler.getPassTypeSelectingUserAction(); // 2) pass 선택 StudyCafePass selectedPass = selectPass(passType); // 3) 사물함 선택 (고정석 only) StudyCafeLockerPass lockerPass = maybeSelectLocker(selectedPass); // 4) 주문 객체 생성 & 결과 출력 StudyCafeOrder order = new StudyCafeOrder(selectedPass, lockerPass); outputHandler.showOrderResult(order); }이점: 각 단계가 메서드로 분리되어 이해하기 쉬움. passType별로 중복된 분기 로직이 크게 줄었음.      2. 할인 및 총금액 계산 로직을 전담하는 객체를 만들어 로직을 명확히 했습니다.Before: 할인금액, 총액 계산 로직이 OutputHandler.showPassOrderSummary 안에 위치.After: StudyCafeOrder라는 도메인 객체가 이 로직을 담당.public int getDiscountAmount() { return (int) (selectedPass.getPrice() * selectedPass.getDiscountRate()); } public int getTotalPrice() { int discountPrice = getDiscountAmount(); int lockerPrice = (lockerPass != null) ? lockerPass.getPrice() : 0; return selectedPass.getPrice() - discountPrice + lockerPrice; }이점: 필요 시 “할인 정책”이 바뀌어도 StudyCafeOrder만 수정하면 되며, 출력부는 깔끔하게 유지.   3. 파일 접근 방식을 인터페이스와 구현체로 나누어 유지보수와 확장성을 높였습니다.Before: StudyCafeFileHandler라는 클래스에서 직접 파일 접근 + 변환.After: StudyCafeRepository(추상) + StudyCafeFileRepository(구현).StudyCafePassMachine은 StudyCafeRepository repository에만 의존 → DIP 준수.향후 DBRepo나 InMemoryRepo 추가 시 StudyCafeRepository만 구현하면 됨. 회고리팩토링을 통해 코드 가독성과 유지보수성이 눈에 띄게 향상되었습니다. 특히 객체지향 원칙(SRP, DIP)을 철저히 적용한 결과, 각 객체가 단 하나의 명확한 책임을 가지면서, 향후 변경이 발생해도 최소한의 수정만으로 대응 가능해졌습니다. 규칙의 절대성이 아닌 "적정선"을 찾는 것이 매우 중요하다는 점을 깊이 깨달았습니다.🔗 리팩토링 미션 깃허브 링크회고스스로 칭찬하고 싶은 점학습한 내용을 즉시 코드에 적용하며 원칙을 체화하고자 노력한 점단순한 기능적 개선을 넘어, 구조적이고 근본적인 리팩토링을 고민한 점아쉬웠던 점 & 보완할 점코드 리뷰에서 코드 확장성과 유지보수성을 높이기 위한 설계가 부족했음을 느꼈습니다. 다음에는 코드 확장성과 유지보수성을 고려한 구조를 더욱 더 고민해 보겠습니다.지나치게 추상화에 치우치는 경향을 느꼈고, 다음에는 더 실용적인 균형을 유지하려 합니다.다음 주 학습 목표다음 주부터는 TDD 기반으로 더욱 실질적이고 구체적인 테스트 코드 작성을 연습할 계획입니다.리팩토링과 테스트가 결합된 실습을 통해, 코드를 더욱 안정적으로 관리하는 방법을 익힐 예정입니다.이번 주 스터디를 통해 "코드는 언제나 적정한 수준의 추상과 구체 사이에서 균형을 잡아야 한다"는 중요한 원칙을 다시 한번 실감했습니다. 앞으로도 적절한 추상화와 구체적인 구현 사이의 "적정선"을 끊임없이 고민하며, 클린하고 안정적인 코드를 지속적으로 작성하는 개발자가 되도록 노력하겠습니다.감사합니다!

백엔드인프런워밍업클럽스터디백엔드클린테스트코드발자국회고3기

제갈진우

[인프런 워밍업 클럽 스터디 3기] PM 워밍업클럽 2주차 회고

학습 내용 요약이번 주차 강의에서는 PM이 고객과 데이터를 어떻게 다뤄야 하는지에 대해 배웠다. 고객과 직접 인터뷰하고 사용성을 테스트하는 것이 중요하며, 데이터를 통해 문제를 파악하고 해결책을 찾는 것이 핵심이었다. 단순히 지표 하나에 의존하지 않고, 여러 데이터를 종합적으로 분석하는 역량이 필요함을 다시금 깨달았다. 나의 회고데이터 분석가로서 PM 역할을 이해하고 학습하는 과정에서 가장 와닿았던 부분은 고객에 대한 전문가가 되는 것과 데이터를 통해 문제를 정의하고 해결하는 것이었다.현재 비게임 산업 확장을 고민하며 고객을 탐색하고 있는 상황에서, 강의에서 배운 고객 인터뷰와 사용성 테스트 방법론은 실무에서 바로 활용할 수 있는 인사이트를 제공했다. 특히, 고객 인터뷰를 할 때 열린 질문을 활용하고, 특정한 선택지로 유도하지 않는 것이 중요하다는 점이 인상적이었다. 실제로 고객과 대화를 하다 보면 원하는 방향으로 답변을 유도하고 싶은 유혹이 있는데, 이번 학습을 통해 그런 실수를 줄일 수 있을 것 같다. 미션 회고이번 미션은 고객 인터뷰 설계였는데, 실무에서도 고객사 인터뷰를 진행하고 있는 만큼 흥미롭게 접근할 수 있었다. 조사 주제를 설정하고, 인터뷰 대상을 정하며, 질문을 설계하는 과정에서 강의에서 배운 프레임워크를 적용해볼 수 있었다. 특히, 사업 질문 → 리서치 질문 → 인터뷰 질문으로 이어지는 논리적인 구조를 유지하는 것이 유용했다. 앞으로 실제 인터뷰를 진행할 때도 이런 방식을 적용해 볼 계획이다. 앞으로의 계획이번 학습을 통해 PM이 고객과 데이터를 어떻게 활용해야 하는지 더 깊이 이해하게 되었다. 현재 고민 중인 비게임 산업 확장과 신규 고객 확보 전략에도 이 개념들을 적용할 예정이다.고객 인터뷰를 활용하여 비게임 산업의 주요 니즈 파악데이터 네이밍 및 이벤트 정의 방식 개선 PM과 데이터 분석의 경계에서 균형을 찾고, 보다 효과적인 문제 해결 방안을 도출하는 것이 앞으로의 목표다. 이번 학습을 통해 그 방향성을 더 명확히 잡을 수 있었다.

기획 · PM· PO워밍업클럽회고2주차발자국PMPO인터뷰고객분석

바다다다

[인프런 워밍업 스터디 클럽 3기 FE] 1주차 미션 회고

1주차 발자국은 여기에: [인프런 워밍업 스터디 클럽 3기 FE] 1주차 발자국발자국 글이 길어져 미션을 따로 회고를 작성하였다.Mission1 - 음식 메뉴 앱Demo (클릭연결)SourceCode (클릭연결) 문제해결첫 미션 이여서 조금 설렜다(?)주어진 과제 안내 동영상에서 기본 화면 + 버튼으로 리스트를 화면에 뿌려주는 형식을 보고 따라 만들어 보았다.맥도날드를 좋아해서 맥도날드의 메뉴로 앱을 구성했다.정적 웹페이지로 구성된 화면이다보니 Github Page를 통해 배포까지해서 Demo화면을 구성하였다.고도화마지막 주차에 React 미션에서 쇼핑몰 앱 구현 미션이 있던데 해당 미션에서 구현한 화면으로 맥도날드 주문 앱을 구성해보아도 괜찮을 거 같다는 생각 회고가장 기본적인 HTML, CSS, JS를 활용해서 만든 웹 페이지라 맛보기에 좋았다.Mission2 - 가위 바위 보 앱Demo (클릭연결)SourceCode (클릭연결)문제해결플레이어는 가위, 바위, 보 3가지 중 한가지를 버튼 클릭 방식으로 제출하고, 컴퓨터는 랜덤으로 가위, 바위, 보를 제출하여 게임의 득점을 계산 한다.10회의 가위 바위 보 게임이 끝나면 최종 결과를 출력하는 앱이다.추가로 컴퓨터와 플레이어의 게임 결과를 출력되도록 화면 구성을 하였다.고도화가위 바위 보를 텍스트가 아닌 아이콘으로 표시하면 사용자가 좀 더 식별하기 쉬울 거 같다.현재는 마지막 단계에서 새로고침을 직접 해야하지만, 새로고침 버튼을 추가하여 사용성을 높일 수 있을거 같다.뒷 부분에서 webSocket을 사용한 채팅 앱도 미션으로 나와있던데 webSocket을 통한 양방향 게임도 구현할 수 있는 하나의 방향이라 생각한다.회고JS문법을 조금 더 많이 활용해볼 수 있었던 미션이다.CSS를 구성하는게 어려웠다ㅠ Mission3 - 퀴즈 앱Demo (클릭연결)SourceCode (클릭연결)문제해결미션 영상에서 안내된 내용대로 구현하려고 했다.여러 개의 답을 랜덤으로 생성하는데 "답이없는 경우"도 있다는 것이다. 우선 정답을 저장해두고 랜덤으로 답안 개수를 생성하면서 저장해둔 정답이 버튼으로 생성되지 않는다면 "답이 없음"버튼을 추가하도록 구현했다.정답을 맞추지 않으면 다음문제로 넘어가지 않도록 하였고, 정답을 제출하면 해당 계산식 문제에서 "?" 물음표가 정답으로 바뀌도록 구현하였다.고도화현재 구현된 버전에서는 정답을 맞추지 않으면 다음 문제로 넘어가지 않기 때문에 "O문제 정답"으로 표기하기 보다 풀이 문제수를 표시하고 있다는게 더 명확할 거 같다.무한대로 문제를 출제하도록 구현하여 문제 풀이의 끝이 없다. 문제수를 설정하고 해당 앱을 시작하는 방법도 생각해볼 수 있겠다.또한, 현재 10이내의 범위에서 덧셈 문제만 출제되고 있는데 좀 더 다양한 퀴즈(수학문제가 아니어도)들을 포함할 수 있다.회고 역시나 CSS로 화면을 표시하는 것에 시간이 많이 필요했다. (버튼 색상 변경, 배경 색 변경 등)alert을 미션에서 처음 띄워보았다. 근데.. 사실 사용자 입장에서는 조금 많이 거슬리는 거 같다. 생각해보면 대부분의 웹사이트에서 alert대신 팝업으로 메시지를 띄워주는 경우가 더 많은 거 같다. Mission4 - 책 리스트 나열 앱Demo (클릭연결)SourceCode (클릭연결)문제해결책 목록에 책을 저장해두고 저장해둔 내용을 아래 테이블에 표시해주는 형태이다.추가적으로 수정기능을 넣었고, 책 배열에서 선택된 객체의 값을 변경하여 이를 구현할 수 있었다.시인성을 높이기 위해 삭제된 책은 책이름이 출력되도록 수정하였다.고도화이게 중요한 책 목록 저장 시스템이라면 DB연동해서 영구적으로 저장해두는게 맞지만, 여기서는 미션이고 JS를 학습하는 목적이니 간단하게 화면에 표시만 했다. 로컬에 저장하는 것을 고려해보면 브라우저 내장 storage를 사용해서 잠깐의 저장 기능도 구현할 수 있지않을까라는 생각을 했엇다.회고앞에 3번 정도 미션을 수행하니 이제 CSS도 조금 적응한 듯 싶다.DB연동을 해보지 못해서 조금 아쉬웠지만, 일단 기초를 다지는 단계이니까 기초에 좀 더 집중하자는 생각. Mission5 - Github Finder 앱Demo (클릭연결)SourceCode (클릭연결)문제해결fetch함수로 github API를 사용하여 사용자의 정보를 얻어올 수 있다.얻어온 데이터를 화면에 적절히 뿌려주면되는 과제 였는데, 추가로 repository로 연결되도록 구현하였다.안내 동영상에는 사용자의 input을 감지하여 입력이 발생될 때 마다 API호출을 해서 화면이 랜더링 되도록 구현한 것으로 추측되는데, 이를 좀 더 효율성을 높이고자 사용자가 input을 입력하고 제출할 때 API call을 한 번 하도록 수정하였다.고도화현재 보여지는 profile정보 등은 간략하게 표시하고 있다.github API를 활용하여 클론 코딩도 가능할 거 같다. 회고외부 API를 호출해서 미션을 진행하며 잘 구현된 API가 화면을 구현하는 데에 많은 도움이 된다는 것.<끝>

프론트엔드워밍업스터디3기프론트엔드미션JSJavascriptHTMLCSS회고

보키

[인프런 워밍업 클럽 3기 - BE/Project] 1주차 회고 발자국 🐾

https://code-boki.tistory.com/231먼저, 회고글을 작성하기 전에 회고법으로 사용할 수 있는 전략에 대해서 예전에 작성한 글이 있어서 다시 한번 공유해본다.종종 이런것들을 프레임워크라고 부르기도 한다.디자인 프레임워크, 회고 프레임워크, 백엔드 프레임워크...정해진 틀에 맞춰서 진행하는 것이다.나는 이 중 회고에 사용되는 KPT 회고 프레임워크를 선택해서 이번 한 주의 회고록을 작성해봤다.Keep(만족, 지속하고 싶은 부분)맨 처음부터 섹션3까지 쭉 들었다.웹 서비스를 구성하는 요소: Client, Server, Database브라우저에 주소창을 입력하면 발생하는 일: DNS, IP Resolution, Gateway, Server, DBMS웹 프레임워크/라이브러리Spring Framework: MVC Pattern책임분리(Segregation Of Principle): Layered Architecure(3-tier)스프링 Bean & DI(의존성 주입): 생성자, 수정자, 필드 방식HTTP, Request, ResponseHTTP Method: GET, POST, PUT, PATCH, DELETEHTTP Status Code: 2xx, 3xx, 4xx, 5xxREST API: 자원, 행위, HATEOASSend Data Client to Server: Query Parameter(Query String), Path Variable, Request BodyDBMS: RDB / NoSQLRDB Key: PK(Primary Key), FK(Foreign Key), Candidate KeyRDBMS: Oracle, MySQL, PostgreSQLNoSQL Management System: MongoDB(Document), Redis(Key/Value)JPA/ORMTransaction, Commit, RollbackPersistence Context(영속성 컨텍스트): JPA's 버퍼/캐싱 레이어/임시 메모리호흡이 조금 빠르긴 했지만, 웹 백엔드 개발에 대한 전반적인 지식에 대해서 배울 수 있는 요약본 강의였다.이대로 쭉 마지막 강의까지 듣고 미션도 쭉죽 완성해 나가길 기대한다!Problem(부족, 아쉬웠던 부분)사실 Backend개발에 앞서 Web이란것도 HTTP 0.9/1/1.1/2.0 프로토콜 버전.. TCP/UDP, 네트워크 OSI 7계층, TCP/IP 4계층, 스위치/허브/라우터 등등 알아야 할 것들도 많고Spring쪽으로 넘어오면 Servlet(Java -> Jakarta) 버전부터 한때 JSP(Java Servlet Page)의 출현으로 인한 SSR 발전부터 현재의 CSR부터 다시 SSR(Next.js)등의 개발세계의 히스토리도 풀어줬으면 어땠을까 하는 생각이 들었다.하지만, 비전공자나 코딩이 처음인 사람들 입장에서는 How it Works보다는 How to Use가 중요하니까 이런 포맷으로 준비한게 아니실까 싶다. 그리고 내가 처음 코딩을 배우는 입장이였다면, 위에 나온 내용들이 개발 지식에는 꼭 필요한 것이라 생각해서 잘 구성한 것 같다고도 생각이 들었다.+ Java나 Kotlin 언어적인 것에 대한 내용도 아주 살짝 있었으면 좋았을 것 같았다!Try(도전, P에 대한 해결책 등으로 다음번에 보완하거나 시도할 부분)나름대로 잘 해 나가고 있다고 생각한다. 개인 프로젝트와 더불어서, 강의에 나온 것들 중에서 내가 원래 했던 백엔드 개발에서 놓쳤거나 추가하고 싶은 부분이 있다면 보완해보기도 하고 강의 프로젝트도 차근차근 함께 만들어나가봐야겠다!

백엔드인프런워밍업클럽스터디회고코틀린스프링백엔드

suover

인프런 워밍업 클럽 스터디 3기 - 백엔드 클린 코드, 테스트 코드 1주차 발자국

Readable Code: 읽기 좋은 코드를 작성하는 사고법Practical Testing: 실용적인 테스트 가이드강의와 함께한 인프런 워밍업 클럽 스터디 3기 - 백엔드 클린 코드, 테스트 코드 (Java, Spring Boot)1주차 발자국 입니다.학습 내용 요약이번 주 동안 [Readable Code: 읽기 좋은 코드를 작성하는 사고법] 강의를 통해 크게 두 가지 주제를 학습했습니다.1⃣ 추상과 구체추상(abstract)은 코드나 로직에서 세부 구현을 생략하고 핵심 의미만 전달하는 방식입니다.구체(concrete)는 코드나 로직의 세부적이고 구체적인 구현을 모두 드러내는 표현 방식입니다.이 개념을 코드에 적용하면 다음과 같은 장점이 있습니다.추상화된 이름 짓기와 메서드 분리를 통해 복잡한 코드를 단순화할 수 있습니다.협업 시 팀원에게 필요한 수준의 정보를 전달할 수 있어 효율적입니다.코드 유지보수가 용이해지고 가독성이 높아집니다.2⃣ 논리와 사고의 흐름, SOLID 원칙📌 논리, 사고의 흐름Early Return: 불필요한 else를 제거하고 조건이 맞지 않을 때 즉시 return해 코드 흐름을 명료하게 유지하는 방법입니다.사고의 Depth 줄이기: 중첩 분기문과 반복문의 깊이를 줄이고, 변수를 필요한 곳 가까이에 선언해 인지적 부담을 낮춥니다.부정어 최소화: 긍정적이고 직관적인 표현으로 메서드 이름을 짓고, 부정 연산자(!) 사용을 줄입니다.📌 SOLID 원칙SRP(단일 책임 원칙): 클래스가 하나의 명확한 책임만 가지도록 설계해야 합니다.OCP(개방-폐쇄 원칙): 기존 코드를 수정하지 않고 확장할 수 있도록 설계합니다.LSP(리스코프 치환 원칙): 자식 클래스가 부모 클래스를 대체해도 프로그램이 정상 작동하도록 설계합니다.ISP(인터페이스 분리 원칙): 사용하지 않는 메서드를 구현하도록 강요받지 않도록 인터페이스를 작게 분리합니다.DIP(의존성 역전 원칙): 고수준 모듈과 저수준 모듈 모두 추상화된 인터페이스에 의존하여 유연성을 높입니다.미션 해결 과정 & 회고Day 2 미션: 추상과 구체 예시미션추상과 구체 강의를 듣고, 생각나는 추상과 구체의 예시 작성해결 과정일상적인 예시를 통해 추상과 구체 개념을 명확히 이해하려고 했습니다.회사 출근이나 배달 음식 주문 등 일상 사례를 통해, 간략히 표현된 추상과 세부적인 표현을 비교하여 정리했습니다.관점 및 이유개념을 일상적이고 친숙한 예시로 접근함으로써 이해를 높이고자 했습니다.추상과 구체의 차이를 분명히 보여줄 수 있는 사례를 선택했습니다.회고일상적 예시 덕분에 개념 이해가 훨씬 쉬워졌고, 추상화의 필요성과 구체적 표현의 중요성을 체감할 수 있었습니다. 앞으로 코드 작성 시 이 관점을 계속 유지할 생각입니다.🔗 Day 2 미션 블로그 링크 Day 4 미션 1: 코드 리팩토링미션중첩 if-else 구조의 주문 검증 메서드 리팩토링접근 관점코드의 인지적 복잡성을 줄이기 위해 Early Return 방식을 사용했습니다.각 조건이 독립적이며, 직관적으로 위에서 아래로 읽히도록 조건 검사를 순서대로 배치했습니다.추가로 주문(Order) 객체가 자기 책임을 다하는 방식으로 책임을 위임하는 방법도 고려했습니다.해결 방법public boolean validateOrder(Order order) { // 주문 항목 유무 if (order.getItems() == null || order.getItems().isEmpty()) { log.info("주문 항목이 없습니다."); return false; } // 총 가격 검증 if (order.getTotalPrice() <= 0) { log.info("올바르지 않은 총 가격입니다."); return false; } // 고객 정보 검증 if (!order.hasCustomerInfo()) { log.info("사용자 정보가 없습니다."); return false; } // 여기까지 왔다면 유효한 주문 return true; }회고Early Return을 적용한 결과, 가독성이 확연히 높아졌고 코드를 읽는 과정이 더 명확해졌습니다. 앞으로도 복잡한 조건 로직을 만날 때 적극적으로 이 방법을 적용할 생각입니다. Day 4 미션 2: SOLID 원칙 정리미션SOLID 원칙을 자신만의 언어로 정리해결 과정각 원칙을 "왜 이런 원칙이 필요한지" 스스로에게 질문하며 이해했습니다.실제 코드 예시를 작성하여, SOLID 원칙을 위반한 코드와 준수한 코드를 비교하면서 학습했습니다.회고원칙을 "내 언어"로 설명하면서, 이전에 막연히 알고 있던 개념들이 훨씬 더 명확하게 다가왔습니다. 단순히 원칙을 외우는 것과 달리, 직접 코드를 작성하고 리팩토링 과정을 거치며 더 깊이 이해할 수 있었습니다.🔗 Day 4 미션 블로그 링크회고이번 주 스터디를 통해 코드를 작성할 때 "추상과 구체의 적정선을 찾는 것이 중요하다"는 점을 깊이 깨달았습니다. 특히 코드가 너무 구체적이면 가독성이 떨어지고, 너무 추상적이면 구체적인 동작이 모호해진다는 점을 직접 실습과 미션을 통해 경험할 수 있었습니다. 또한 Early Return이나 중첩 최소화 등 작은 코드 개선만으로도 가독성이 크게 높아지는 것을 체감할 수 있어 유익했습니다.스스로 칭찬하고 싶은 점미션을 통해 배운 내용을 바로 적용하여 리팩토링하면서 개념을 내 것으로 만들려고 노력한 점이 뿌듯했습니다.SOLID 원칙을 단순히 암기하는 것이 아니라, 스스로 이해하고 예시 코드까지 작성하면서 명확히 내 언어로 표현한 것이 좋았습니다.아쉬웠던 점 & 보완할 점가끔 너무 추상화에 집중하다 보니 코드가 실제 구현에서 너무 멀어지는 경우가 있어, 구체성과 추상성을 더 균형 있게 다루는 연습이 필요하다고 느꼈습니다.SOLID 원칙을 실제 프로젝트 코드에 적용해 보지 못한 점은 조금 아쉬웠습니다. 앞으로 개인 프로젝트에 적용하여 체득할 계획입니다.다음 주 학습 목표다음 주는 강의의 다음 섹션인 "리팩토링" 부분을 학습할 예정입니다. 특히 리팩토링 실습을 통해 SOLID 원칙을 실제 코드에 적용하고, 객체 지향적 사고방식을 내 것으로 만드는 것을 목표로 삼고 있습니다.소감이번 주 강의를 통해 코드 가독성이라는 주제가 얼마나 중요하고 광범위한지 실감했습니다. 앞으로도 이번 학습 내용을 프로젝트에 적극 적용하며, 읽기 좋고 유지보수하기 좋은 코드를 꾸준히 작성하는 습관을 들이겠습니다.감사합니다!

백엔드인프런워밍업클럽스터디백엔드클린테스트코드발자국회고

leeebug

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

인프런 워밍업 클럽 스터디 3기 풀스택 과정을 시작하고 정신차려보니 벌써 금요일이 되었다.1주 차는 본격적으로 Next.js를 다루기 위한 워밍업 단계라고 생각한다. 이번주는 개발 환경을 어떻게 구축하는지와 Next.js의 주요 기능 등을 빠르게 학습했다. 또한, React Query, Recoil 등의 상태 관리 라이브러리의 기본적인 사용법도 강의를 들으면서 복기하였다.과제의 경우, 먼저 빠르게 해결할 수 있는 부분을 최대한 완성하고, 추가 기능을 구현하는 데 집중했다. 동시에 UI와 UX를 개선하는 것을 이번 주 목표로 삼아 보다 완성도 높은 결과물을 만들고자 했다. 📝 1주 차 학습SupabaseNoSQL 기반의 Firebase의 대체제로, PostgreSQL 기반의 BaaS를 제공하는 플랫폼서버리스 환경에서 빠르게 백엔드를 구축할 수 있도록 지원데이터베이스, 인증, 스토리지, 실시간 기능, 서버리스 함수, 관리 대시보드 지원Next.jsReact 기반의 풀스택 개발을 하기에 최적화 된 웹 프레임워크SSR, SSG를 지원API 라우트 기능을 제공하여 별도의 백엔드 서버 없이도 간단한 API 기능 수행이미지 최적화, 자동 코드 분할을 지원하여 개발 생산성을 높임서버 액션을 지원하여 API 라우트 없이도 클라이언트에서 서버 코드 호출 가능레이아웃 또는 컴포넌트별로 메타 데이터 설정 가능TailwindCSS유틸리티 퍼스트 CSS 프레임워크미리 정의된 클래스를 조합하여 스타일을 적용하는 방식 (tailwind.config.ts에서 커스텀 클래스 정의 가능)JIT 컴파일을 지원하여 실제로 사용된 클래스만 포함하여 CSS 파일 크기 최적화 적용반응형 디자인 지원(sm:, md:, lg: 등)다크 모드 및 테마 설정 지원(dark: )공식 및 커스텀 플러그인을 활용하여 다양한 추가 기능 제공React Query(Tanstack Query)데이터 패칭, 캐싱, 동기화 등을 쉽게 관리할 수 있게 도와주는 React 라이브러리로 주로 서버 상태 관리 용도로 사용자동 데이터 패칭, 자동 캐싱, 자동 리패칭을 제공하여 API 서버 호출을 최소화로 관리Mutation을 통한 데이터 수정쿼리 무효화를 통해 데이터를 최신 상태로 유지RecoilReact 상태 관리 라이브러리로 Context API보다 유연하고 확장성 있는 방법을 제공전역 상태 관리, 컴포넌트 간 상태 공유, 파생 상태 계산, 비동기 작업 처리 기능 제공2025.3.7 기준 약 2년간 업데이트가 이루어지지 않고 있기 때문에, Zustand, Jotai, RTK 등의 대체 라이브러리 추천 📋 1주 차 미션💬 깃헙 저장소🚀 과제 시연 영상 보러가기미션 해결 과정 요약1주 차 미션은 Next.js와 React Query 그리고 TailwindCSS를 사용하여 Todo List 앱 만들기였다. 기본적인 CRUD 기능을 구현하는 과제로, 목표 추가, 목표 수정, 완료 표시, 삭제 등의 작업을 통해 상태 관리와 비동기 데이터 처리를 익히는데 워밍업 미션으로 아주 적합하다고 생각한다. 강의에서 구현한 Todo List를 기반으로 앱을 구현하면서 총 세 가지에 초점을 맞춰서 진행하였다.CSS Transition을 사용하여 UX 개선CSS Transition을 활용하여 인터페이스의 상호작용을 부드럽고 직관적으로 처리스켈레톤 UI를 적용하여 UI와 UX 개선초기 렌더링 시 목표 리스트가 출력되는 부분에 스켈레톤 UI를 적용하여, 어느 부분에 컨텐츠가 표시될지에 대한 명확한 시각적 피드백 제공React Query 캐싱과 디바운스를 활용하여 불필요한 API 호출 감소초기 렌더링 시 데이터를 패칭하는 부분과 키워드 검색 시 데이터 패칭하는 부분이 동일한 액션을 공유하여 키워드 입력 시마다 불필요한 데이터 패칭이 지속적으로 이루어지는 문제를 인지하였음.export async function getTodos(): Promise<TodoRow[]> { const supabase = await createServerSupabaseClient() const { data, error } = await supabase .from('todos') .select('*') .order('created_at', { ascending: true }) if (error) { handleError(error) } return data ?? [] }해당 로직의 문제점은 키워드 입력시마다 즉시 API 요청이 발생하여 검색 결과가 매번 새로고침되는 증상을 발견하였고 키워드 검색 시 디바운스를 적용하여 의도적으로 API 요청을 지연시켜서 약간의 API 요청 감소 효과가 발생하였음 const [searchInput, setSearchInput] = useState('') const [debouncedSearchInput, setDebouncedSearchInput] = useState('') // 검색어 입력 디바운스 useEffect(() => { const timer = setTimeout(() => { setDebouncedSearchInput(searchInput) }, 600) return () => clearTimeout(timer) }, [searchInput])의도적으로 600ms의 딜레이를 발생시켰음에도 여전히 검색 시 마다 API 요청이 발생하여, 키워드 입력 시마다 스켈레톤 UI가 표시되어 개선 방법을 고민하던 중에 키워드 검색 기능과 데이터 패칭 액션 기능을 분리시키기로 결정하였음 const filteredTodos = todosQuery.data?.filter((todo) => todo.title.toLowerCase().includes(debouncedSearchInput.toLowerCase()) )결론적으로 초기 렌더링 시 Todo List 데이터 패칭 액션으로 호출하는 방식으로 유지하고, 키워드 검색 기능은 캐싱된 데이터를 별도로 필터링하는 로직으로 분리하여 불필요한 API 요청이 감소하는 효과를 가져옴 👀 1주 차 회고첫 주부터 다사다난한 일주일을 보냈다. 그동안 사용했던 Next.js가 사실 React에 더 가까운 구조였음을 깨닫게 된 한 주였다. 다시 시작하는 마음으로, 시간 날 때마다 틈틈이 복습하고 복기하는 과정을 반복해야겠다는 생각이 든다.이번 워밍업 클럽의 목표는 Next.js의 심화 기능을 익히는 것과 그동안 신경 쓰지 않았던 UI와 UX 개선에 힘쓰는 것이다.스켈레톤 UI, CSS Transition, 캐싱을 활용한 API 호출을 최소화하는 방법 등을 활용하는 방법과 더불어서 React Suspense, Optimistic UI 등의 기능을 이번 워밍업 클럽 과제와 접목하여 실제 프로젝트에 적용해 볼 예정이다. 

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

[워밍업 클럽 2기 - Clean Code & Test Code] 4주차 발자국

워밍업 클럽 2기: Clean Code & Test Code의 4주차 발자국 작성입니다.3주차 발자국 보러가기 📝 학습 내용Presentation Layer 테스트 작성Mock더 나은 테스트를 작성하기 위한 여러 팁REST Docs ✍ 학습 내용 복습Q. Presentation Layer의 특징은?클라이언트로 부터 입력을 받아서 비즈니스 계층으로 해당 요청을 보내는 계층요청을 제일 먼저 받는 계층입력 데이터에 대한 기본적인 검증을 수행한다Presentation 계층에서의 검증과 Business 계층에서의 검증을 분리해서 생각해야 한다Presentation 계층에서는 보통 형식적인 검증을 한다예시: 필수 입력 값 검사, 데이터 타입 검사, null 검사, 빈 문자열 검사Business 계층에서는 비즈니스 로직에 따른 도메인 유효성 검사가 이루어진다Business 계층으로 부터 결과를 받아서 클라이언트로 반환한다컨트롤러에서 사용하는 요청 DTO가 서비스 계층으로 침투하지 못하도록 컨트롤러 계층에서 서비스 전용 DTO로 변환하는 것을 권장한다(상황에 따라 다를 수 있을것 같다. 만약 받는 포맷이 변할 가능성이 거의 없다면, 그냥 컨트롤러의 DTO를 쭉 사용해도 괜찮지 않을까 생각이 된다.)Q. Presentation Layer의 테스트 방법은?Business, Persistence 계층을 모킹해서 테스트한다MockMvc 같은 도구를 사용해서 HTTP요청과 응답을 시뮬레이션 한다모킹을 위해서 Mockito 같은 프레임워크를 사용할 수 있다 Q. Test Double의 종류를 정리해보자면?Dummy: 아루런 동작도 하지 않는 객체. 잘 사용되진 않지만, 보통 파라미터 전달용으로 사용된다.Fake: 실체 객체와 동일한 기능은 수행하지만, 프로덕션 용도로 사용하기에는 적합하지 않은 객체.예시: 인메모리로 맵을 사용해서 가짜 레포지토리를 구현하는 경우Stubs: 테스트에서의 요청에 대해 미리 준비된 결과를 제공하는 객체. 미리 반환할 데이터가 정의되어 있고, 호출하는 경우 해당 데이터를 반환한다. 미리 정의되어 있지 않은 것들은 응답하지 않는다.Spies: Stub이지만 정복 기록도 함께하는 객체. 호출 여부, 호출 횟수 등의 정보를 기록할 수 있다. 일부는 실제 객체 처럼 동작하고, 일부는 Stubbing할 수 있다.Mocks: 행위에 대한 기대를 명세하고, 그 명세에 따라 동작되도록 설계 된 객체. 그러니깐 개발자가 직접 그 객체의 행동을 관리하는 객체이다.Q. Stub과 Mock을 구분하는 기준은?Stub : 상태 검증(State Verification)Mock : 행동 검증(Behavior Verification) TIP. 테스트 작성을 위한 여러 팁을 정리해보면Mockito 프레임워크를 한번 래핑하는 BDD Mockito 프레임워크를 사용해서 조금 더 자연스러운 API 네이밍으로 프레임워크를 사용할 수 있다테스트 간의 독립성을 보장하자 테스트에서 전역 변수를 정의해서 사용하는 것은 권장하지 않는다@BeforeEach또는 @AfterEach 메서드를 사용한 레포지토리 클렌징@Transactional사용 Test Fixture용 클래스를 따로 분리해서 사용하는 것은 권장하지 않는다Test Fixture를 @BeforeEach를 사용한 셋업 메서드에서 사용하는 경우, 중복 제거보다 해당 테스트에 해당 내용을 알아야하는지 고려해보고 적용하자테스트 내용은 동일한데 입력값만 변경해보면서 테스트 해보고 싶으면 @ParameterizedTest 사용private메서드에 대한 테스트가 필요하다면 해당 메서드의 책임을 분리할지 고민해본다단순히 테스트하기 위해서 public으로 열어두는 것은 권장하지 않는다 Q. REST Docs vs Swagger의 차이는?REST Docs테스트를 통과해야 문서가 만들어지기 때문에 신뢰도가 높다프로덕션 코드에 비침투적이다코드의 양이 많고 설정이 상대적으로 어렵다Swagger적용이 쉽다문서에서 바로 API 호출이 가능하다애노테이션을 달아줘야 하기 때문에 프로덕션 코드에 침투적이다🤔 회고워밍업 클럽의 마지막 주차가 되었다. 강의 양이 많아도 내가 정말 필요한 내용을 담아서 만들어져있어서, 시간 가는 줄 모르고 시청했다.강의와 미션을 따라가면서 학습에 많은 도움을 받았다. 만약 워밍업 클럽 3기가 있다면 다시 참가할 예정이다.지금까지 학습 내용을 다시 복습해보고 더 나아가서 프로젝트에 적용하는 것이 목표이다. 🔍 참고Practical Testing: 실용적인 테스트 가이드

백엔드테스트코드워밍업클럽2기백엔드발자국4주차회고

채널톡 아이콘