블로그

치현

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

학습 내용인프런 워밍업 클럽 스터디 2주차로, 이번 주는 드롭 박스 프로젝트와 함께 Supabase의 Storage를 다뤄볼 수 있는 시간이었다. Supbase Storage1. 기본 구성 요소Files: 모든 종류의 미디어 파일 저장 가능 (이미지, GIF, 비디오 등)Folders: 파일을 체계적으로 구성하기 위한 디렉토리 구조Buckets: 파일과 폴더를 담는 최상위 컨테이너 (접근 규칙별로 구분)2. 접근 제어 모델Private Buckets (기본값) RLS(Row Level Security) 정책을 통한 접근 제어JWT 인증 필요Signed URL을 통한 임시 접근 가능Public Buckets파일 조회 시 접근 제어 없음URL만 있으면 누구나 접근 가능업로드/삭제 등 다른 작업은 여전히 접근 제어 적용3. 보안 기능RLS 정책 설정 가능SELECT (다운로드)INSERT (업로드)UPDATE (수정)DELETE (삭제)소유권 관리owner_id 필드로 리소스 소유자 추적JWT의 sub claim 기반 소유권 할당4. 이미지 변환 기능 (Pro Plan 이상)실시간 이미지 최적화크기 조정품질 조정 (20-100)WebP 자동 최적화변환 옵션resize 모드: cover, contain, fillwidth/height 지정 (1-2500px)최대 파일 크기: 25MB최대 해상도: 50MP5. 인증 방식S3 액세스 키서버 사이드 전용모든 버킷에 대한 완전한 접근 권한세션 토큰클라이언트 사이드 사용 가능RLS 정책 기반 제한된 접근6. 통합 기능Next.js 이미지 로더 지원AWS S3 호환성PostgreSQL DB와 연동7. 제한사항파일명은 AWS S3 명명 규칙 준수 필요HTML 파일은 보안상 plain text로 반환이미지 변환 기능은 Pro Plan 이상에서만 사용 가능미션 2 구현 내용과제 구현 저장소Dropbox 중파일의 마지막 수정(업로드) 시간을 표시하는 기능 추가 하기 export interface FileObject { name: string bucket_id: string owner: string id: string updated_at: string created_at: string last_accessed_at: string metadata: Record<string, any> buckets: Bucket }=> DropboxImage 컴포넌트가 prop으로 받는 image의 타입은 FileObject로 그 중 업로드시간은 created_at을 의미하기에 이를 이미지에 추가하였다.(사진 참고) 포인트 1: 한글 파일명 es-hangul 사용// 안전한 파일명 생성을 위한 유틸리티 export class FileNameConverter { // 안전한 문자 패턴 정의 private static readonly SAFE_CHARACTERS = /^[a-zA-Z0-9!\-_.*'()]+$/; private static generateRandomString(length: number = 8): string { const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; return Array.from({ length }, () => chars.charAt(Math.floor(Math.random() * chars.length)) ).join(""); } // 파일명이 안전한 문자들로만 구성되었는지 확인 private static isSafeFileName(name: string): boolean { return this.SAFE_CHARACTERS.test(name); } // 안전하지 않은 문자를 포함한 파일명을 안전한 형식으로 변환 private static convertToSafeFileName(name: string): string { try { // 파일명 정규화 const normalized = name.trim().normalize(); // 한글이나 특수문자가 있는지 확인 const hasKorean = /[ㄱ-ㅎㅏ-ㅣ가-힣]/.test(normalized); const hasSpecialChars = /[^A-Za-z0-9]/.test(normalized); if (!hasKorean && !hasSpecialChars) { return normalized; } // 한글이 있는 경우 로마자로 변환 시도 if (hasKorean) { const romanized = romanize(normalized); if (romanized && romanized !== normalized) { // 로마자 변환 결과에서 안전하지 않은 문자 제거 return romanized.replace(/[^a-zA-Z0-9]/g, "_").toLowerCase(); } } // 변환 실패 시 랜덤 문자열 생성 return this.generateRandomString(); } catch (error) { console.error("Conversion error:", error); return this.generateRandomString(); } } // 원본 파일명을 안전한 형식으로 변환 static encode(fileName: string): string { console.log("Original filename:", fileName); const extension = fileName.split(".").pop() || ""; const nameWithoutExt = fileName.slice(0, fileName.lastIndexOf(".")); const safeName = this.isSafeFileName(nameWithoutExt) ? nameWithoutExt : this.convertToSafeFileName(nameWithoutExt); console.log("Safe filename:", safeName); return `${safeName}_${Date.now()}.${extension}`; } // 파일명에서 타임스탬프 제거하여 원본 이름 추출 static decode(fileName: string): string { const [name] = fileName.split("_"); return name || fileName; } }포인트 2 : 업로드 날짜 표시export function formatDate(timestamp: string): string { const date = new Date(timestamp); const now = new Date(); const diff = now.getTime() - date.getTime(); // 1일 이내 if (diff < 24 * 60 * 60 * 1000) { const hours = Math.floor(diff / (60 * 60 * 1000)); if (hours < 1) { const minutes = Math.floor(diff / (60 * 1000)); return `${minutes}분 전`; } return `${hours}시간 전`; } // 30일 이내 if (diff < 30 * 24 * 60 * 60 * 1000) { const days = Math.floor(diff / (24 * 60 * 60 * 1000)); return `${days}일 전`; } // 그 외 return date.toLocaleDateString("ko-KR", { year: "numeric", month: "long", day: "numeric", }); } 회고파일명 변환하는데 생각보다 시간이 많이 소요됐다.여찌저찌 구현은 헀지만, 이미지가 어떻게 encoding되고 decoding되는지 일련의 과정에 대한 공부가 필요함을 느끼는 이번주 였다.  

풀스택풀스택인프런워밍업스터디클럽Next3기SupabaseReact프론트엔드2주차발자국

Masocampus

[GEN AI 인사이트] AI도 실수한다? 그래서 등장한 CriticGPT!

AI는 완벽할까요? 사실 그렇지 않아요! AI도 실수를 하고, 오류를 범할 수 있죠. 🤖그래서 나온 것이 CriticGPT입니다! AI가 생성한 코드 속 오류를 찾아내는 AI, CriticGPT를 함께 알아볼까요?CriticGPT는 GPT-4 기반 AI로, ChatGPT가 생성한 코드에서 오류를 찾아내는 역할을 해요.AI가 스스로의 실수를 바로잡는 시대, 신기하지 않나요? 😃AI의 답변이 점점 더 정교해질수록, 그 안에 숨은 오류를 찾는 일이 더 어려워지고 있어요.✅ ChatGPT의 답변이 점점 더 정확해지면서 오류 검출이 복잡해짐✅ 강화 학습 과정에서 AI 트레이너의 부담 증가💡 그래서 등장한 CriticGPT! AI의 답변을 분석하고 오류를 지적하도록 학습되었어요.기존에는 사람이 직접 AI의 오류를 찾아야 했어요. 하지만 실수 가능성이 있었죠.❌ 일반 검토 방식 → 사람이 직접 오류를 찾음 → 실수 가능성 증가⭕ CriticGPT 활용 → 보다 철저한 검토 가능 → 불필요한 오류 지적 감소👉 AI 트레이너가 CriticGPT를 활용하면, 보다 정확하고 신뢰도 높은 피드백을 작성할 수 있어요!CriticGPT는 단순한 AI가 아니에요! 꾸준한 학습을 통해 점점 더 정교한 오류 탐지 능력을 갖추고 있어요. AI 트레이너가 일부러 코드에 버그 삽입 CriticGPT가 오류를 찾아내고 비평 작성 여러 비평을 비교하며 정확성 높은 피드백을 학습 이를 반복하며 더 정밀한 오류 탐지 능력을 갖춤이렇게 꾸준한 학습을 통해 AI의 신뢰도를 높이는 역할을 하고 있어요! 😊🔹 Step 1: ChatGPT가 생성한 코드 샘플 선택🔹 Step 2: 코드에 버그 삽입 → CriticGPT가 오류를 찾아내도록 학습🔹 Step 3: 여러 비평을 생성 후 분석 → 오류 검출률, 정확성, 신뢰도를 평가👉 이 과정을 반복하면서 CriticGPT는 점점 더 똑똑해져요! 🚀CriticGPT도 완벽한 것은 아니에요. 몇 가지 한계가 존재하죠.⚠ 짧은 코드 위주로 학습됨 → 긴 코드 분석 능력 부족⚠ 환각 현상(hallucination) 발생 → 존재하지 않는 오류를 생성할 가능성⚠ 복잡한 문제 평가 어려움 → AI가 정확하게 평가하지 못할 수도 있음하지만 이러한 한계를 극복하기 위한 추가 연구가 진행 중이에요! 앞으로 더 발전할 CriticGPT, 기대되지 않나요? 😃💡 CriticGPT 덕분에 AI의 신뢰도가 더 높아지고 있어요!✅ AI 검토 AI의 등장 → CriticGPT가 AI 오류를 잡아냄✅ 정확한 피드백 제공 → AI 트레이너의 부담 완화✅ 더 발전하는 AI → CriticGPT 덕분에 더욱 신뢰할 수 있는 AI 시대AI의 가능성을 깨우는 마소캠퍼스와 함께, 더 스마트한 AI 활용법을 배워보세요! 😊마소캠퍼스와 함께 AI를 활용해 업무 혁신을 이뤄보세요! 효율적이고 스마트한 일의 방식을 통해 성장할 수 있도록 도와드릴게요. 📌 관련 강의 <ChatGPT 최신 모델 프롬프트 엔지니어링 바이블>실전형 프롬프트 엔지니어링을 익히고, AI 챗봇(ChatGPT)을 활용해 성과 극대화!

AI 업무 활용aiai도구criticgptai오류인공지능ai검토ai학습마소캠퍼스ai활용gpt

김경환

워밍업 클럽 3기 BE 클린코드&테스트 - 1주차 발자국

회고저는 그동안 제가 이상적인 코드를 작성하고 있다고 생각했습니다. 나름 많은 고민을 코드에 녹여냈고 가독성을 항상 신경썼습니다. 하지만 이번 강의를 통해서 제가 많은 부분들을 몰랐다는 점에서 놀랐습니다. 섹션 3을 통해 전반적인 안 좋은 습관들을 교정해나갈 기회는 저에게 값집니다. 남은 시간들도 기대가 됩니다. 강의 내용 요약인트로우리는 왜 이 강의를 듣는가우리는 코드를 읽는 시간을 코드를 쓰는 시간보다 더 많이 할애한다우리가 읽어야 하는 코드 :여러 사람이 작성한 코드내가 한시간 전에 작성한 코드읽기 좋은 코드는 더 나은 코드 작성을 위해 필수적이다 코드를 잘 짠다는 것은?읽기 좋은 코드를 작성하는 것"코드는 작성한 순간부터 레거시다."코드의 독자 :미래의 동료미래의 나읽기 어려운 코드는 추후의 모두에게 악영향을 미친다이 강의에서는 읽기 좋은 코드를 위해 어떤 관점으로 어떻게 접근해야 좋을지 이야기한다 추상우리가 클린 코드를 추구하는 이유클린 코드를 추구함으로써 가독성을 확보할 수 있다가독성이 높으면 글이 잘 읽힌다= 이해가 잘된다가독성이 높으면 코드가 잘 읽힌다= 이해가 잘된다= 유지보수하기가 수월하다= 우리의 시간과 자원이 절약된다.클린 코드를 작성하기 위해 우리는 추상화에 집중해야 한다  추상과 구체추상이란?어떤 모습에서 형상을 뽑아내는 것구체적인 정보에서 어떤 이미지를 뽑아내는 것특정한 측면만을 가려내어 포착하는 것특정한 측면 외 나머지는 버린다는 것중요한 정보는 남기고, 덜 중요한 정보는 생략하여 버린다. 추상화 레벨추상화 정도에 따라 레벨이 나뉜다추상화 레벨이 높을 수록 중요한 부분만 남기고 나머지는 제한다. 추상화의 가장 대표적인 행위이름 짓기 이름 짓기이름 짓기프로그래머가 가장 힘들어하는 일이름을 짓는다는 행위는 추상적 사고를 기반으로 한다.추상적 사고표현하고자 하는 구체에서 정말 중요한 핵심 개념만을 추출하여 잘 드러내는 표현우리 도메인의 문맥 만에서 이해되는 용어 이름 짓기 유의 사항단수와 복수 구분하기말미에 '-(e)s'를 붙여 어떤 데이터가 단수인지 복수인지를 명확히 하는 것만으로도 읽는 이에게 중요한 정보를 같이 전달할 수 있다.이름 줄이지 않기줄임말이라는 것은 가독성을 제물로 바쳐 효율성을 확보하는 것보통 이름을 줄임으로써 얻는 것보다 잃는 것이 많아 자제하는 것이 좋다다만 관용어처럼 많은 사람들이 자주 사용하는 줄임말이 있다.이런 줄임말이 이해될 수 있는 바탕은 문맥에 있다. 은어/방언 사용하지 않기특정 집단에서만 이해될 수 있는 은어 사용 금지기준 : 새로운 사람이 팀에 합류했을 때 이 용어를 단번에 이해할 수 있는가?도메인 용어 사용하기이 경우 도메인 용어를 먼저 정의하는 과정 (ex.도메인 용어 사전)이 선행되어야 할 수 있다이상적인 표현을 좋은 코드들을 통해 습득하기비슷한 상황에서 자주 사용하는 단어, 개념 습득하기ex. pool, candidate, threshold 등 메서드와 추상화한 문단의 주제는 반드시 하나다잘 쓰여진 코드 또한 하나의 주제만을 가진다생략할 정보와 의미를 정하고 드러낼 정보를 구분해야 한다.메서드 선언부메서드명추상화된 구체를 유추할 수 있는, 적절한 의미가 담긴 이름파라미터와 연결 지어 더 풍부한 의미를 전달할 수도 있다.파라미터파라미터의 타입, 개수, 순서를 통해 의미를 전달파라미터는 외부 세계와 소통하는 창반환 타입메서드 시그니처에 납득이 가는, 적절한 타입의 반환값 돌려주기메서드의 반환 타입만 보고도 바로 이해가 되어야 한다. void 대신 충분히 반환할만한 값이 있는지 고민해보기void로 충분할 경우도 있지만 가급적 반환값 사용하기반환값을 둘 경우 테스트도 용이해진다. 추상화 레벨하나의 세계 안에서는, 추상화 레벨이 동등해야 한다. 매직 넘버, 매직 스트링상수를 추출한다는 것의 의미이름을 추출한다는 것은 그 자체로 추상화상수도 이와 같다매직 넘버, 매직 스트링이란?의미를 갖고 있으나, 상수로 추출되지 않은 숫자, 문자열 등상수 추출로 이름을 짓고 의미를 부여함으로써 다음의 이점들을 확보할 수 있다.가독성유지보수성 뇌 메모리 적게 쓰기멀티 태스킹은 곧 저글링을 하는 것과 다름이 없다 (책 '도둑맞은 집중력' 발췌)사람은 한번에 하나의 일에만 집중할 수 있다.또한 하던 일을 다른 일로 전환할 경우 그에 따른 전환 비용이 발생한다.우리가 읽기 가장 좋은 코드는 한번에 읽히는 코드이다.이를 위해 아래 세가지 요소는 지양해야 한다.이해하려면 기억해야 하는 정보낮은 추상화 레벨불필요한 정보 Early return조건문을 사용할 때 else if 나 else를 사용할 경우사용자는 else의 선행 조건을 파악하기 위해 앞선 if와 else if들을 확인해야 한다.이는 사용자가 이해를 위해 기억력을 할당해야 하는 안 좋은 패턴이다.else if 와 else 는 지양해야 한다.가급적 지양코드가 짧은 등의 이유로 가독성의 문제가 없을 경우는 예외이다.같은 이치로 switch문도 가급적 지양해야 한다.  사고의 depth 줄이기중첩 분기문, 중첩 반복문중첩되는 분기문과 반복문은 함수로 따로 빼는 것이 좋을 수 있다.함수로 분리함으로써 읽는 사람으로 하여금 사고의 깊이를 줄여 가독성을 높여준다.다만 간단한 중첩문, 분기문의 경우 오히려 분리하지 않는게 좋다.사용할 변수는 가까이 선언사용된 변수가 20줄이 넘어가는 이전의 코드에서 선언될 경우읽는 입장에선 해당 변수의 존재를 확인하기 위해 다시 20줄 위로 올라가야 한다.변수 사용부와 선언부를 가까이 하여 가독성을 높이자 공백 라인을 대하는 자세공백 라인도 의미를 가진다.복잡한 로직의 의미 단위를 나누어 읽는 사람에게 추가적인 정보를 제공할 수 있다. 부정어를 대하는 자세부정 연산자의 경우 가독성이 떨어진다독자로 하여금 사고의 반전을 강제한다.부정어 대처법부정어구를 쓰지 않아도 되는 상황인지 체크부정의 의미를 담은 다른 단어가 존재하는지 고민부정어구로 메서드명 구성 해피 케이스와 예외 처리예외를 대하는 자세예외가 발생할 가능성 낮추기어떤 값의 검증이 필요한 부분은 주로 외부 세계와의 접점인 점에 유의하기ex. 사용자 입력, 객체 생성자, 외부 서버의 요청 등의도한 예외와 예상하지 못한 예외를 구분하기사용자에게 보여줄 예외와 개발자가 직접 보고 처리해야 할 예외 구분Null을 대하는 자세항상 NullPointException을 방지하는 방향으로 경각심 가지기메서드 설계 시 return null 자제하기만약 어렵다면 Optional 사용을 고려Optional을 대하는 자세Optional은 비싼 객체꼭 필요한 상황에서만 활용Optional을 파라미터로 받지 않도록 한다이 경우 분기 케이스가 세가지나 된다Null인 경우Null이 아닌 경우Optional 자체가 Null인 경우Optional을 반환 받았다면 최대한 빠르게 해소한다Optional을 해소하는 방법분기문을 만드는 isPresent()-get() 대신 풍부한 Optional의 API 사용ex. orElseGet(), orElseThrow(), ifPresent(), ifPresentOrElse()orElse(), orElseGet(), orElseThrow()의 차이를 숙지해야 한다orElse() : 항상 실행, 확정된 값일 때 사용orElseGet() : null인 경우 실행, 값을 제공하는 동작 정의 추상의 관점으로 바라보는 객체 지향객체란?추상화된 [데이터 + 코드]관심사의 분리특정한 관심사에 따라서 객체를 만들어낼 수 있다.관심사에 따라 기능과 책임을 나눈다나눈 관심사를 바탕으로 어플을 만든다.이를 통해 유지보수성을 높일 수 있다.높은 응집도, 낮은 결합도특정한 관심사끼리 응집도가 높아야 한다관심사 내의 기능들 간의 결합도가 낮아야 한다뜻A를 수정했을 때 B가 큰 영향을 받아선 안된다. 객체 설계하기객체로 추상화하기사용자는 객체의 내부 로직을 알 필요가 없다 공개 메서드 선언부를 통해 외부 세계와 소통하고 나머지 필드나 로직들은 비공개를 함으로써 캡슐화한다.객체의 책임을 나눔으로써 객체 간 협력을 유도한다.객체가 제공하는 것절차 지향에서 잘 보이지 않았던 개념의 가시화관심사를 한 군데에 모음으로써 높은 응집도 확보= 유지보수성 증가객체를 사용하는 입장에선 구체적인 내부 구현을 신경쓰지 않고 높은 추상화 레벨의 도메인 로직을 다룰 수 있다.새로운 객체를 만들 때 주의할 점1개의 관심사로 명확하게 책임이 정의되어 있는 지 확인하기 setter 사용 자제객체 내부에서 외부 세계의 개입 없는 방식을 추구함으로써 의도치 않은 버그를 사전에 방지사용이 필연적일 경우 'set~'이라는 이름 대신 'update~'와 같은 더 명확한 이름을 고려getter 사용 자제setter 와 같은 이치로 직접 꺼내서 사용하기보다 가급적 객체 내에서 해결하게끔 설계getter의 경우 setter와는 달리 필요할 경우 사용해도 된다.필드의 수 최소화불필요한 데이터가 늘수록 복잡도가 올라 유지보수성이 낮아진다.기존의 필드들을 통해 계산할 수 있는 기능들은 메서드를 통해 제공하자.단, 미리 계산하는 것이 성능상의 이점을 가질 경우 필드로 사용 가능 SOLIDSRP단일 책임 원칙 (Single Responsibility Principle)하나의 클래스는 단 한가지의 변경 사유만을 가져야 한다변경 사유 = 책임 = 관심사SRP 원칙을 잘 지킬 경우의 이점 :관심사의 분리높은 응집도낮은 결합도OCP개방-폐쇄 원칙 (Open-Closed Principle)확장에는 열려있고 수정에는 닫혀 있어야 한다.기존 코드의 변경 없이, 시스템의 기능을 확장할 수 있어야 한다.필수 요소 :추상화다형성LSP리스코프 치환 원칙 (Liskov Substitution Principle상속 구조에서, 부모 클래스의 인스턴스를 자식 클래스의 인스턴스로 치환할 수 있어야 한다.자식 클래스는 부모 클래스의 책임을 준수하고부모 클래스의 행동을 변경하지 않아야 한다LSP를 위반할 경우의 문제점 :어플리케이션 오동작예상 밖의 예외위 두 문제를 방지하기 위한 불필요한 타입 체크 동반ISP인터페이스 분리 원칙 (Interface Segregation Principle)클라이언트는 자신이 사용하지 않는 인터페이스에 의존하면 안된다.인터페이스를 잘게 쪼개자.기능 단위로 인터페이스를 나눠서 사용하자.ISP를 위반할 경우의 문제점 :불필요한 의존성으로 인한 결합도 상승DIP의존성 역전 원칙 (Dependency Inversion Principle)상위 수준의 모듈은 하위 수준의 모듈에 의존해서는 안된다.추상화에 의존해야 한다.의존성의 순방향 : 고수준 모듈이 저수준 모듈을 참조의존성의 역방향 : 고수준, 저수준 모듈이 모두 추상화(인터페이스, 추상 클래스)에 의존DIP를 잘 지킬 경우 저수준 모듈이 변경되어도 고수준 모듈에는 영향이 가지 않는다. 상속과 조합상속보다는 조합을 사용해야 한다.상속은 시멘트처럼 굳어지는 구조다.상속의 단점수정이 어려움부모와 자식 간의 결합도가 높음부모가 수정될 경우 모든 자식들에게 영향조합과 인터페이스를 활용하여 유연한 구조를 얻을 수 있다.상속을 통한 코드 중복 제거가 주는 이점보다 중복이 생기더라도 유연한 구조 설계가 가능한 조합이 주는 이점이 더 크다. Value Object도메인의 어떤 개념을 추상화하여 표현한 값 객체값으로 취급하기 위해서 아래 세 가지 요소를 보장해야 한다.불변성final 필드 사용setter 금지동등성서로 다른 인스턴스여도(=동일성이 달라도), 내부의 값이 같으면 같은 값 객체로 취급equals() & hashCode() 재정의 필요유효성 검증객체가 생성되는 시점에 값에 대한 유효성 보장VO vs EntityVO와 Entity의 가장 큰 차이점은 식별자 유무이다Entity식별자가 있다식별자만 같으면 다른 필드가 달라도 동등한 객체로 취급식별자가 다르지만 필드가 다를 경우 시간이 지남에 따라 변화한 것으로 취급VO식별자가 없다내부의 모든 값이 다 같아야 동등한 객체로 취급이는 곧, 전체 필드가 식별자 역할을 한다고 볼 수 있다. 일급 컬렉션컬렉션을 포장하면서 컬렉션만을 유일하게 필드로 가지는 객체컬렉션을 다른 객체와 동등한 레벨로 다루기 위해 사용한다단 하나의 컬렉션 필드만을 가진다컬렉션을 추상화하여 의미를 담을 수 있고, 가공 로직의 보금자리가 생긴다.가공 로직에 대한 테스트도 작성할 수 있다.만약 컬렉션을 반환해야 할 경우 새로운 컬렉션을 반환해야 한다.기존의 컬렉션을 변경할 여지를 없앤다. Enum의 특성과 활용Enum은 상수의 집합상수와 관련된 로직을 담을 수 있는 공간상태와 행위를 한 곳에서 관리할 수 있는 추상화된 객체특정 도메인 개념에 대해 그 종류와 기능을 명시적 표현 가능만약 변경이 잦은 개념은 Enum보다 DB로 관리하는 것이 나을 수 있다. 숨겨져 있는 도메인 개념 도출하기도메인 지식은 만드는 것이 아니라 발견하는 것객체 지향은 현실을 100% 반영하는 것이 아닌 흉내내는 것이다.이를 통해 현실 세계에서 쉽게 인지하지 못하는 개념도 도출해서 사용할 수 있다.완벽한 설계라는 것은 불가능하다근시적, 거시적 관점에서 최대한 미래를 예측해야 한다시간이 지나 만약 틀렸다는 것을 인지할 경우를 상정하고 코드를 작성해야 한다.미션Day 2미션 설명"추상과 구체"의 강의를 듣고 생각나는 추상과 구체의 예시가 있다면 한번 3~5문장 정도로 적어봅시다. 일상 생활, 자연 현상, 혹은 알고 있는 개발 지식 등 어느 것이든 상관 없습니다. 추상에서 구체로, 또는 구체에서 추상으로 방향은 상관 없으나, 어떤 것이 추상이고 어떤 것이 구체 레벨인지 잘 드러나게 작성해 보아요:) 나의 답1.추상코드를 꼽는다구체기기에 연결되어 있는 코드를 콘센트의 두 구멍에 맞춰 연결함으로써 전력을 공급한다.2.추상커피를 마신다구체커피가 담긴 컵을 손으로 잡아 고정시킨 상태에서 컵의 각도를 조절하여 커피를 입에 주입한다.3.추상지하철을 탄다구체지하철 역사의 입구를 찾아 들어간 후 전철에 탑승하여 원하는 정거장에서 하차한다. Day 4미션 1 설명1. 아래 코드와 설명을 보고, [섹션 3. 논리, 사고의 흐름]에서 이야기하는 내용을 중심으로 읽기 좋은 코드로 리팩토링해 봅시다.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 static final String NO_ORDER_ITEM = "주문 항목이 없습니다."; public static final String INVALID_TOTAL_PRICE = "올바르지 않은 총 가격입니다."; public static final String NO_USER_INFO = "사용자 정보가 없습니다."; public boolean validateOrder(Order order) throws OrderException { if (order.doesHaveItem()) { throw new OrderException(NO_ORDER_ITEM); } if (order.doesNotHaveValidTotalPrice()) { throw new OrderException(INVALID_TOTAL_PRICE); } if (order.doesNotHaveCustomerInfo()) { throw new OrderException(NO_USER_INFO); } return true; }Orderpublic abstract class Order { public abstract boolean doesHaveItem(); public abstract boolean doesNotHaveValidTotalPrice(); public abstract boolean doesNotHaveCustomerInfo(); }변경 사항if문의 조건들을 하나의 함수로 정의함으로써 조건의 의미를 명확히 했습니다.if문의 조건에 부합하지 않을 경우 바로 결과를 반환하게 했습니다.불필요한 부정 조건을 제거했습니다.별개의 예외 처리 클래스를 생성하여 예외를 명확히 했습니다.관심사를 기준으로 공백을 두었습니다.미션 2 설명SOLID에 대하여 자기만의 언어로 정리해 봅시다. SSRP (단일 책임 원칙)하나의 클래스는 하나의 책임(=관심사)을 가져야 한다.SRP를 지킴으로써 객체들을 관심사 기준으로 분리할 수 있다.높은 응집도와 낮은 결합도를 제공한다.응집도클래스나 모듈 내 요소들이 긴밀하게 연관되어있는 정도결합도한 요소가 변경되었을 때 다른 요소들이 영향을 받는 정도OOCP (개방-폐쇄 원칙)확장에는 열려 있고, 수정에는 닫혀 있어야 한다.기존 코드의 변경 없이도 시스템의 기능을 확장할 수 있어야 한다.추상화와 다형성을 활용함으로써 구현할 수 있다.LLSP (리스코프 치환 원칙)두 클래스가 상속 구조를 가질 때 부모 클래스의 인스턴스를 자식 클래스로 치환하여도 기능 상에 문제가 없어야 한다.자식 클래스는부모 클래스의 책임을 준수해야 한다.부모 클래스의 행동을 변경하지 않아야 한다.LSP를 위반할 경우 아래 문제가 발생할 수 있다.오동작예상 밖의 예외위 두 문제를 방지하기 위한 불필요한 타입 체크 동반IISP (인터페이스 분리 원칙)클라이언트는 자신이 사용하지 않은 인터페이스에 의존하면 안된다.ISP를 위반할 경우불필요한 의존성으로 인해 결합도가 높아진다.특정 기능의 변경이 여러 클래스에 영향을 미칠 수 있다.필요한 기능 단위로 인터페이스를 나눠서 사용해라.DDIP (의존성 역전 원칙)상위 수준의 모듈은 하위 수준의 모듈에 직접 의존해서는 안된다.추상화(인터페이스, 추상 클래스)에 의존해야 한다.의존성의 순방향고수준 모듈이 저수준 모듈을 직접 참조의존성의 역방향고수준, 저수준 모듈 모두 추상화를 참조DIP를 지킬 경우 저수준 모듈의 변경이 고수준 모듈에 영향을 미치지 않게 된다. 

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

회고록 섹션 4. 객체 지향 패러다임을 들으며 미션을 통해 아직 많이 미숙하지만 SOLID에 대해 좀 더 가까워지는 계기가 되었다. public class Mission { 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 MyExplanation { public boolean validateOrder(Order order) { if(order.getItems().size() == 0) { log.info("주문 항목이 없습니다."); return false; }else { return validateOrderDetails(order); } return true; } private static boolean validateOrderDetails(Order order) { if(hasNonZeroTotalPrice(order)) { return isCustomerInfoPresent (order); } log.info("올바르지 않은 총 가격입니다."); return false; } private static boolean isCustomerInfoPresent (Order order) { if(order.hasCustomerInfo()) { return true; } log.info("사용자 정보가 없습니다."); return false; } private static boolean hasNonZeroTotalPrice(Order order) { return order.getTotalPrice() > 0; } }이런식으로 풀어봤다. 현재는 여기까지가 최대이지만 나중에 강의를 다 듣고 한번 더 리펙토링 해보며 이전 코드와 비교해보는 시간을 가져볼것이다.[인프런 워밍업 클럽 스터디 3기_백엔드 클린 코드, 테스트 코드] 

서나무

[회고] PM 인프런 워밍업 클럽 3기 1주차

학습 내용 요약PM의 선호 역량은 기업마다 다르지만, 핵심은 제품으로써 고객 가치와 사업적 가치를 만들어내는 것고객 중심의 고객이 만족하는 제품을 만들어야 함PM은 제품 성과 전반을 책임지는 사람상황과 맥락에 맞는 방법론을 적용할 수 있어야 함 PMF(Product Market Fit): 시장을 만족시키는 제품을 찾기 > Feature Work(새로운 가치 창출) / Growth Work(더 많은 사용자 만들기)PM은 문제를 잘 정의해야 하고, 그 문제가 정말 중요한 것인지 판단해야 함데이터는 문제 파악에 기여, 문제 해결은 창조의 영역이며 좋은 아이디어가 필요 학습 회고사실 나는 챌린지에 참여하기 전에 미리 학습을 했는데, 강의 내용이 너무 알차고 좋아서 멈출 수 없었다...사용자의 니즈를 파악하는 방법에 대한 막연함이 있었는데 이 강의를 통해 막혀있던 것이 뚫린 느낌?지금 진행중인 사이드 프로젝트에 적용할 수 있는 것들이 많을 것 같다. 다만 분석툴을 익혀야 한다는 장벽이 있지만 그래도 해본 것과 시도조차 안한 것의 차이는 크다고 생각해서 한번 배워보려고 한다.노트에 필기를 했었는데 이렇게 회고 글로 정리해서 작성하니 더 학습한 내용을 상기할 수 있어서 좋다.미션 회고PM의 역할에 대한 아티클을 읽고 내 생각을 정리하는 미션이었는데, 처음엔 구글에 검색에서 마구잡이로 잡히는대로 읽다가 영양가 있는 글이 적다고 느껴져서 요즘IT에서 검색해서 읽었다.요즘IT는 정성이 담긴 글들이 많아서 좋은 아티클들이 많다고 생각했고, 그래서 그런지 강의에서 배운 내용들과 일치하는 내용이 많았다.

기획 · PM· PO

정예은

[워밍업클럽3기] 클린코드-박우빈 발자국 1주차

학습내용섹션1~4📝미래의 나를 위해, 미래의 자손을 위해이름 짓기는 깔쌈하게 ! 중요키워드만 뽑아서 !중요한 정보만 남기는 추상화 잘 하기 !메서드 생성클린코드를 위해 각 로직별로 추상화를 하여 메서드로 만들어주자!✅“한가지 역할” 을 하는 코드 블럭을 찾고, 메서드로 분리✅그에 맞는 메서드 “이름 “ 지어주기✅<aside> 💡⭐메서드 생성 단축키 =ctrl+alt+m</aside>   학습정리 링크https://www.notion.so/DAY02-1ab010f075ca81ed8b20fd23dead0c76?pvs=4https://www.notion.so/DAY-04-SOLID-1-1ab010f075ca81abbcd8c909d84e74ce?pvs=4👣회고👣이번 주는 SOLID 원칙을 중심으로 코드 리팩토링을 진행하며, 보다 견고하고 유지보수하기 쉬운 구조를 고민하는 시간을 보냈다. 💡 잘한 점✅ 메서드 추출을 통해 가독성을 높이고 코드의 역할을 명확히 함✅ 기존 코드를 무조건 변경하기보다는, 확장 가능성을 고려하면서 구조를 잡아나감✅ 인터페이스와 추상 클래스의 활용을 고민하며 유연한 설계를 연습함 ⚠ 아쉬운 점아직은 강사님이 따라하는 대로 코드를 있는 그대로 따라치기만 하는 과정으로 수업을 들었음하나하나씩 로직과 메서드들을 분석해가며 수업을 들으려니, 30분 수업은 나에게 60분이되어 돌아왔음그만큼 시간을 오래 잡아먹기 때문에 진도 맞추기가 너무 어려웠다..내가 이 로드맵을 참여한게 올바른 선택이긴 할까? 라는 고민도 많이 들었지만, 일단 코드 100번정도 따라쳐보면 대충 흐름이 파악되지 않을까? 생각하며 수업을 듣고 노션에 정리하던 한주였다....  🎯 다음 주 목표단순히 원칙을 따르는 것이 아니라, 상황에 맞는 적용법을 체득하기미션을 해결할 때, "왜 이렇게 설계했는가"를 먼저 고민하고 코드를 작성하는 습관 들이기   📢미션📢Day02추상 : 눈사람을 만든다 구체 :대기중에 떠다니는 먼지가 핵이 되어, 이 핵을 중심으로 수증기가 응결해가며 형성되는 결정체의 집합체를 손으로 뭉친다2덩이로 둥글게 뭉쳐서 몸통과 머리로 붙여준다주변에 굴러다니는 , 자연에서 산출되는, 생물이 아닌 단단한 고체 물질을 눈과 코에 붙여준다  Day04SOLID원칙단일책임원칙클래스는 하나의 책임만 가져야 한다.책임을 인지하고 분리하고 다른 클래스 만들기.메인 도입부에 게임 실행부 넣지 않고 → 지뢰찾는 로직을 담은 클래스를 하나 생성해서 하나의 책임만 갖도록 Minesweeper 개방 폐쇄 원칙기존 코드를 많이 변경하지 않고 확장할 수 있도록 설계하기 추후 유지보수나 조건들이 추가로 생겨날때 당황하지 않도록 너무 상수로만 값이나 데이터 정의 내리지 않기리스코브 치환 원칙자식은 부모를 대체해서 일할 수 있고, 부모는 자식을 대체할 수 없다. 부모 클래스를 사용하는 곳에 자식 클래스를 넣어도 문제가 없어야 함인터페이스 분리 원칙하나의 커다란 인터페이스 사용하는게 아니라, 여러개의 인터페이스로 분리하기 하나의 인터페이스에는 하나의 메서드만 , 관련된 메서드만 넣어야함의존성 역전 원칙구체적인 구현 클래스가 아니라, 인터페이스나 추상 클래스에 의존 하도록 설계  public boolean validateOrder(Order order) { if (isInvalidOrder(order)) { return false; } return true; } private boolean isInvalidOrder(Order order) { if (order.doesNotHaveAnyItem()) { log.info("주문 항목이 없습니다."); return true; } if (order.doesNotHaveCustomerInfo()) { log.info("사용자 정보가 없습니다."); return true; } if (order.hasNegativeTotalPrice()) { log.info("올바르지 않은 총 가격입니다."); return true; } return false; } 👣회고👣미션을 해결하면서 "추상화"의 중요성을 몸소 체감한 한 주였음특히, 눈사람 만들기 예제를 통해 구체적인 행동을 추상화하는 연습을 했고, 이를 코드에도 적용하려 노력했다. 

백엔드워밍업클럽워밍업클럽3기박우빈백엔드백엔드스터디지뢰찾기클린코드리팩토링

[인프런 워밍업 클럽 3기 - BE 클린 코드, 테스트 코드] 1주차 발자국 👣

1주차 발자국 👣 💻 강의 수강👩🏻‍💻 학습 내용 요약잘 읽히는, 이해할 수 있는 코드를 작성하자.추상화는 구체적인 정보들로터 중요한 정보들만 남기는 것이다. 추상화에서 도메인의 문맥이 중요하다.이름을 짓는 행위도 추상화의 일종으로, 단어만으로 읽는 이에게 정보를 쉽게 전달할 수 있어야 한다.메서드의 주제는 반드시 하나여야 한다. 포괄적인 의미를 담아야 하며, 작은 단위의 메서드로 쪼갤 수 있다.상수와 줄 바꿈으로도 가독성을 높일 수 있다.코드를 읽는 이가 너무 많이 고민하지 않게, 기억해야 하는 정보가 많지 않게 읽을 수 있게 하자.Early return, 사고 과정의 depth 줄이기, 부정어 대체하기 등예외가 발생할 가능성을 낮추자. 의도한 예외와 예상하지 못한 예외를 구분하자.Java에서 null로 인한 예외를 주의하자. Optional 사용 고민해 보자.  🤔 학습 내용에 대한 회고강의를 듣고 이해하며 미션을 수행하는데 시간이 빠듯했다.하나하나 기록하며 강의를 듣는 편인데 이러한 방식은 시간이 오래 걸린다. 어떻게 해야 적은 시간 내에 강의 수강 및 이해, 미션 수행이 가능할 지 학습 방법에 대해서 고민해봐야겠다.강의가 너무 재밌다!!!💙 🎯 다음 주 학습 목표효율적인 학습 방법을 알아내서 적용해보자!!  ✉ 미션💭 미션 해결 과정Day 2 미션 우리 가족 귀염둥이와 산책하는 것에 대해서 구체화해보았다. Day 4 미션1번 내용)강의에서 언급한 논리, 사고의 흐름에 따라서 읽기 좋은 코드가 되도록 리팩토링하였다.2번 내용)SOLID 원칙에 대해서 나만의 언어로 작성하였다. 🤔 미션 해결에 대한 간단한 회고미션을 잘 한 건지 모르겠다.. 피드백을 받고 싶다...  📚 출처[강의] Readable 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 

백엔드워밍업클럽백엔드clean-code객체지향리팩토링

kailis

발자국 1주차: 읽기 좋은 코드와 현실 비즈니스 속 객체지향의 관계

 해당 글은 인프런 워밍업 클럽 스터디 3기 - 백엔드 클린 코드, 테스트 코드를 진행하며 깊게 고민해 보려고 한 내용을 담습니다. 단순한 이해보다는 현실적 적용에 집중합니다.   우리는 자바를 기반한, 객체지향이라는 패러다임을 담은 개발을 하며 <객체지향>의 중요성을 귀가 닳도록 듣는다. 책임의 분리. 어디까지가 객체의 책임인가? 어떤 것은 추상이고 어떤 것이 구상인가? 책임이라는 개념은 객체지향에서 매우 중요하지만, 그와 동시에 현실에서 적용하는 데에서는 생각할 부분이 많다. 나는 비즈니스 일정과 아름다운 코드, 그리고 복잡성에 대한 사이에서 그동안 고민을 많이 해 왔었다. 강의를 들으면서 그것들을 고민하고자 했고, 그 고민에 대한 이번 주 일주일 동안의 이야기를 해 보려고 한다.  Graceful Code와 Business 사이에서 사실 SOLID? 안다. 내 연차에서 그것을 모르는 자바 개발자는 드물 것이다. 하지만 그 개발자들은 나와 같이 이 SOLID를 어떻게 적용할지에 대해 고민할 것이다. 따라서 나는 이번 주에는 SOLID 원칙을 실무적으로 적용하는 과정에서 현실적인 한계를 분석하는 데 집중하려 했다. 특히, 기존 시스템에 SOLID 원칙을 도입하려 할 때 발생하는 문제점을 파악하고, 이를 해결하기 위한 현실적인 방법을 고민하고자 했다. 그래, 현재 코드베이스의 특성을 고려하여 현실적인 타협점을 찾는 방법을 고민해 보고 싶었다. 강의를 보며 생각해 본 이번 주의 고민들을 녹이며, 지금부터 SOLID 원칙을 한 가지씩 짚어가면서 이야기해 보고자 한다. 원칙과 현실의 부딪힘 SRP(단일 책임 원칙) 적용의 현실적 고민 사실 제일 좋아하는 원칙이다. SOLID의 가장 핵심이라고 생각하기도 한다. 그렇지만 책임이란 단어는 참 모호하다. [카페]에서 [커피]를 [주문]한다고 했을 때, [주문]이 담당하고 있는 책임은 어디까지일까? 이와 같이 나는 SRP를 고민할 때, [어디까지가 객체의 책임]인가에 대해서 고민한다. class CafeOrderService { private PaymentProcessor paymentProcessor; private NotificationService notificationService; private TransportService transportService; public void processOrder(Order order) { paymentProcessor.process(order); notificationService.sendOrderConfirmation(order); transportService.giveDrink(); } } 카페 주문은 [결제] / [완료 안내] / [손님에게 음료 주기]를 포함할 수 있다. 그런데 카페의 할 일이 많아지게 되면, 이 클래스는 너무 많은 책임을 가지게 된다. 만약 카페가 배달 서비스를 시작하면 카페의 책임은 [배달] / [배달 기사님 콜 부르기] / [배달 완료표시] 등으로 확장될 수 있다. 카페의 할 일은 많지만 책임의 갯수가 늘어날 때는 역시 분리를 고민해야 할 것 같다. 그럼 그걸 몇 개로 제한해야 할까? 3개? 4개? … 개수가 아니지 않을까? 내가 이번에 정의를 내리게 된 것은 SRP가 단순하게 ”하나의 클래스 = 하나의 책임“이 아니라. “변경의 이유가 하나인가”를 고민하는 원칙이라는 것이다. 따라서 우리는 카페 주문이라는 서비스가 “언제” 바뀔지에 따라 응집도의 기준을 정해야 한다. 가령 [카페 주문]이라는 것이 여러 방식의 주문을 가질 수 있게 된다면(방식의 개수가 바뀜), [매장 카페 주문] / [배달 카페 주문]으로 바꾸고, 외부 인터페이스는 타입에 따라 분리하도록 지정하는 것이다. // 주문 처리를 위한 상위 인터페이스 interface OrderService { void processOrder(Order order); } // 매장 주문 처리 class StoreOrderService implements OrderService { private PaymentProcessor paymentProcessor; private NotificationService notificationService; private ServeService serveService; @Override public void processOrder(Order order) { paymentProcessor.process(order); notificationService.sendInStoreOrderConfirmation(order); serveService.serveDrinkAtCounter(order); } } // 배달 주문 처리 class DeliveryOrderService implements OrderService { private PaymentProcessor paymentProcessor; private NotificationService notificationService; private DeliveryService deliveryService; private DriverNotificationService driverService; @Override public void processOrder(Order order) { paymentProcessor.process(order); notificationService.sendDeliveryOrderConfirmation(order); deliveryService.prepareForDelivery(order); driverService.notifyAvailableDriver(order); } } // 주문 타입에 따라 적절한 서비스를 선택하는 팩토리 class OrderServiceFactory { private StoreOrderService storeOrderService; private DeliveryOrderService deliveryOrderService; public OrderService getOrderService(OrderType orderType) { switch (orderType) { case STORE: return storeOrderService; case DELIVERY: return deliveryOrderService; default: throw new UnsupportedOperationException("지원하지 않는 타입:" + orderType); } } } // 클라이언트 코드 class CafeOrderController { private OrderServiceFactory orderServiceFactory; public void processOrder(Order order) { OrderService orderService = orderServiceFactory.getOrderService(order.getType()); orderService.processOrder(order); } }  이제 OrderService 입장에서는 책임이 분리됐다. 타입만 ENUM에서 선택해서 넘겨 주면 되겠다. 그런데 여기서 튀어나오는 원칙이 하나 더 있다. OCP다. OCP(개방-폐쇄 원칙) 적용의 현실적 고민 OCP. 인터페이스 정의의 핵심이다. 특히 전략 패턴에서 고민하게 되는 부분인 것 같다. OCP 이야기가 많이 나오는 예제로 Oauth 로그인이 있다. 네이버 / 카카오 인증을 인터페이스로, 행위 중심을 토대로 폐쇄하되 앞으로 여러 가지 인증의 가능성을 넓히는 것. 그런데 나는 확장의 필요성과 지속성을 먼저 고려해야 한다고 생각한다. “우리는 이 객체를 어디까지 확장할 것인가?” 확장성을 높이기 위해 인터페이스를 도입했지만, 너무 많은 추상화가 오히려 코드 가독성을 해치는 경우도 있을 수 있다. 이번에 크리스마스 특집으로 행사를 한다고 한다. 이 행사는 일주일간 일어나고 사라질 것이다. 그럼 우리는 여러 가지 이벤트가 발생할 때마다 늘 DiscountPolicy의 구현체를 추가해 주어야 하는 것일까? 위의 예시에서도, 이벤트성으로 음료 주문 방식에 증정 이벤트가 추가되었다고 해 보자. 그렇다면 증정 이벤트가 추가된 클래스를 만들어야 할까? 뭐, 그럴 수도 있다. 클래스는 쓰다 지우면 된다. 그런데 추가된 클래스를 나중에 삭제할 수 있다고 쉽게 생각하지만, 실제로는 코드베이스에 남아 오히려 누군가 옵션을 더하는 식으로 클래스가 커져, 유지보수 비용이 발생할 수 있다. 같은 소스코드 안에 있다면 3줄로 관리하면 되는데, 클래스를 하나 늘리는 것이 추후 휴먼 오류 가능성을 높이는 행동이 될 수 있다는 것이다. (세상에는 객체지향을 사랑하는 사람만 있지는 않다) 결국 객체의 [개방]을 위해 보장해야 하는 것은 로직이 얼마나 지속될지의 여부인 것 같다. 그렇다면 리스코프 치환 원칙은 어떠한가? LSP(리스코프 치환 원칙) 적용의 현실적 고민 리스코프 치환 원칙의 중심은 부모에게 있다. 부모가 하는 일을 자식이 위반하지 않아야 하는 것이다. 여기서 가장 적용하기 모호해지는 것은 [부모가 하는 일]이다. 부모는 어떠한 책임을 가질까? 그리고 어떠한 행위를 할까? 역할이 모호한 만큼, 부모의 역할 또한 모호하게 느껴진다. 우리의 카페 주문 예시로 생각해 보자. 사람은 카페에게 기대하는 역할이 있다. 나에게 내가 원하는 음료수를 주는 것이다. 그것이 배달이든, 실제로 가서 주문하는 것이든 달라지는 것은 없다. 여기서 가장 중요한 것은 나 / 음료수 / 전달 이다. 나는 리스코프 원칙을 [클라이언트가 기대하는 응답을 주는 것]을 부모가 하는 일을 위반하지 않는 것이라고 생각한다. 우리의 카페 주문에서의 리스코프 책임 원칙은, “부모가 가진 인터페이스의 계약을 지키는 것”은, [음료수를 전달하는 것]일 것이다. 방식은 다르더라도 음료수만 잘 배달하면 된다. 그러면 지킨 것이다. 그렇다면 스프링에서 가장 많이 쓰이는 DIP는 어떨까? DIP(의존성 역전 원칙) 적용의 현실적 고민 DIP는 고수준 모듈이 저수준 모듈에 의존하지 않고 둘 다 추상화에 의존하게 만든다. 스프링 프레임워크에서는 이 원칙을 기반으로 DI(의존성 주입)를 제공한다. 개발자로서 우리는 스프링에게 객체 생성을 위임하고 수많은 DI를 수행한다. new를 직접 호출할 필요가 없다니! 정말 편리하다. 그런데 모든 의존성을 인터페이스로 추상화하는 것이 항상 최선일까? 다음과 같은 상황을 고려해보자. interface UserService { User findById(Long id); void register(User user); } class CafeUserService implements UserService { // 카페 유저 관련 구현 } class StoreUserService implements UserService { // 상점 유저 관련 구현 } 현재 서비스에는 카페 사용자만 존재하고, 상점 사용자는 아직 구현 계획이 없다. 그럼에도 불구하고 “미래의 확장성”을 위해 인터페이스를 도입해야 할까? 인터페이스를 도입하면 분명 유연성을 얻을 수 있지만, 당장 StoreUserService 구현체가 필요하지 않다면 이는 불필요한 복잡성을 가져올 수 있다. 게다가 초기 스타트업에서 이 부분은 두드러진다. 도입 가능성을 예측했으나 갈아엎어지는 기획이 너무나 많으니까... 따라서 DIP 적용의 균형점은 근미래의 변경 가능성 / 팀의 개발 문화에 있다고 생각한다. 물론 대규모 엔터프라이즈 애플리케이션에서는 철저한 DIP 적용이 장기적으로 유리할 수 있다. 작은 프로젝트나 스타트업에서는 과도한 추상화가 오히려 개발 속도를 늦출 수 있다. “도입이 상상되는 인터페이스”는 애자일과 맞지 않는다. 결국 DIP의 적용은 실용적 균형의 문제가 아닐까 생각해 본다. 이제 마지막, ISP에 대해서 생각해 보았다. ISP(인터페이스 분리 원칙) 적용의 현실적 고민 ISP의 케이스에서 가장 경계해야 할 것은 결국 [개수]라는 생각을 한다. 얼마나 분리할 것인가? 얼마나 분리하는 것이 효율적인가? 나는 [현재 상태에서 필요한 만큼]이라고, 그러니까 최소한이라라고 생각한다. 많은 인터페이스가 생겼을 때 가장 큰 문제는 아무래도 파일이 최소 2배가 된다는 점이다. 수많은 파일은 복잡성을 높이는 것이 사실이니까. 어떠한 아키텍처가 이 수많은 인터페이스들을 깔끔하게 감당할 수 있는가? Spring과 같은 DI 프레임워크에서는 이런 문제가 더욱 두드러질 수 있다. 수많은 작은 인터페이스들의 구현체를 모두 Bean으로 등록하고 관리해야 하기 때문이다. 동시에 나는 섣부른 인터페이스 분리를 가장 경계해야 한다고 생각한다. 인터페이스가 재정의되어야 하는 순간, 기존에 해당 인터페이스들을 사용하고 있는 객체의 로직을 전부 다 다시 살펴야 한다. 따라서 ISP 또한... 먼저 상상하지 않는 것이 중요해 보인다. 이 인터페이스를 구현한 뒤 시일이 지난 이후, 다른 구현체에서도 상태가 변하지 않을 인터페이스만을 최대한 고민해 보려 한다. 잠시 응집도가 떨어지더라도.  회고 우리는 현실과 아름다운 코드의 사이에서 무엇을 포기해야 하는가? 객체의 책임은 비즈니스의 상황마다 가변적일 수 있다는 생각을 하기 때문이다. 인터페이스가 많으면 아름답다. 하지만 한눈에 파악하는 것은 어려워진다. 전략패턴은 객체의 책임을 확장성있게 분리한다. 하지만 어떠한 객체를 할당할지 정하는 구체적인 룰이 정해져야 한다. 결국 아름다움은 집단의 룰을 포함한다. 혼자서는 무한대로 아름다울 수 있지만, 같이 하는 현실에서도 아름다움을 추구하는 것이 Readable한 코드의 핵심이라고 생각한다. 비즈니스적으로 빠른 코드 ≠ 읽기 좋은 코드 ≠ 아름다운 코드 장기적인 관점에서 아름다운 코드는 결국 집단이 얼마나 동일한 룰을 체득하고 있는지에 따라 달라진다고 생각한다. 그리고 우빈님의 강의는 그 룰에 대한 가이드라인을 주고 있다는 생각이 들었다. 이 룰을 체득한다면, 적어도 비즈니스를 위한 코드를 생각할 때 객체지향의 관점을 망치지는 않을 것이다. 다음 주 계획 다음 주에는 내가 알고 있는 클린코드와, 강의에서 이야기하는 클린 코드를 적용하면서 TDD를 공부해 볼 예정이다.프로젝트 한 가지를 가지고 와서 이야기를 하면 좋을 것 같아서, Gilded Rose를 가지고 와 봤다.유명한 리팩토링 kata 라이브러리니 프로젝트를 [적용]하는 방식에 있어서 많은 것을 이야기해 볼 수 있을 것 같다.테스트 코드에 대해서도 강의를 베이스로 해서 Junit을 연습해 보고자 한다.   

[인프런 워밍업 스터디] 추상과 객체 지향의 구체화

인프런 ‘Readable Code: 읽기 좋은 코드를 작성하는 사고법’을 수강한 후, 작성한 내용입니다.📌 1주차 기간 강의섹션 2. 추상코드를 잘 짠다는 것은?개발자라면 코드를 잘 짜기 위해 노력한다. ‘코드를 잘 짠다’라는 것은 무엇일까?코드를 잘 짠다는 것은 이해하기 쉬운 코드, 즉 ‘읽기 좋은 코드’라는 것에 동의하지 않는 사람은 없을 것이다.내가 짠 코드를 읽는 대상은 결국 나와 동료이다. 미래의 나와 동료를 위해 매 순간 읽기 좋은 코드를 작성하려고 노력해보자.클린 코드와 리팩토링의 가장 좋은 예시는 테스트 코드 생성 사이클로 볼 수 있다.리팩토링 대상/범위 확인기능 보장을 위한 테스트 코드리팩토링 & 테스트 코드로 검증클린 코드를 추구하는 이유그런데 클린 코드를 추구하는 이유는 뭘까?코드가 잘 읽힌다 ⇒ 이해가 잘 된다 ⇒ 유지보수하기 수월하다 ⇒ 시간과 자원이 절약된다!즉, 클린 코드는 우리의 시간과 자원을 절약해준다.클린 코드, 그리고 추상과 구체추상은 클린 코드를 관통하는 주제이다. 그렇다면 추상이란 뭘까?추상 : 중요한 정보는 가려내어 남기고, 덜 중요한 정보는 생략하여 버린다.추상의 반대편은 구체라고 볼 수 있다. 하나의 예시를 들어보자.질문A랑 주말에 뭐 했어?답변답변 1 : 식당에 예약해서 갔다왔어.답변 2 : 케치테이블을 통해 가고자 하는 식당에 들어가서 가고자하는 날짜와 시간을 선택하고 그때 식당에 들어가서 밥을 먹고왔어.답변 1과 답변 2는 같은 의미를 나타낸다. 그런데 표현은 다르다. 답변 2는 구체적인 사실이고 답변 1은 추상화된 문장이라고 할 수 있다.구체에서 추상으로 갈수록 정보는 함축되어 제거되고, 추상에서 구체로 갈수록 생략된 정보를 유추하고 재현한다.이를 개발자답게 컴퓨터 과학에 적용해보자.int는 4 byte이고, char는 1 byte이다. 같은 byte로 이루어지지만, 데이터를 어떻게 읽는지에 따라 달라진다. 즉, 데이터 개념에도 추상화가 존재하는 것이다!잠깐 프로그램의 정의에 대해 생각해보자. 프로그램은 다음과 같이 말할 수 있다.프로그램 = 데이터 + 코드위의 예시를 통해 데이터 개념에 추상화가 존재함을 알 수 있었다. 당연히 코드에도 추상화가 존재한다. 또한 데이터 + 코드에도 추상화가 존재한다.흔히 고수준/저수준 언어 라는 용어를 들어봤을 것이다. 고수준과 저수준이 나뉘는 이유는 추상화에 대한 수준을 나타내기 때문이다. 잊고 있을 수 있지만, 컴퓨터는 0과 1밖에 모른다!적절한 추상화는 복잡한 데이터와 복잡한 로직을 단순화하여 이해하기 쉽게 한다!만약 어느 도시에서 예약을 선택이라고 한다고 가정해보자. 그렇다면 답변 1은 다음과 같이 바뀔 것이다.식당을 선택해서 갔다왔어.이 말을 들으면 무슨 말인가 싶다. 말을 통해 유추하거나 재현하기 쉽지 않다. 단순히 말이 안된다고 생각할 수 있지만 이유에 대해 따져보자.추상화 과정에서 중요한 정보를 남기지 않았다.식당을 방문 날짜를 미리 정하는 예약이 아니라, 단순히 선택했다는 정보만 남김상대적으로 덜 중요한 정보를 남기고 중요한 정보를 삭제해석자가 동일하게 공유하는 Context가 없다.중요한 정보의 기준이나 도메인 영역 별 추상화 기준은 다를 수 있음해당 도시에 살았으면 이해 가능잘못된 추상화가 야기하는 사이드 이펙트는 상당히 크다!적절한 추상화란 해당 도메인의 문맥 안에서 정말 중요한 핵심 개념만 남겨서 표현하는 것이다.이름 짓기이름을 짓는다는 행위는, 추상적 사고를 기반으로 한다.단수와 복수 구분하기이름 줄이지 않기은어/방언 사용하지 않기좋은 코드를 보고 습득하기좋은 이름을 짓기 위해 노력하자!메서드 추상화메서드 이름으로 구체적인 내용을 추상화할 수 있어야 한다.서점에서 책을 샀다.서점에 가서 책을 고른다. 책을 계산대에 가져가 직원에게 건네주고 책 가격 금액을 전달하고 책을 얻었다.잘 쓰여진 코드라면, 한 메서드의 주제는 반드시 하나다!그런데 내용이 다음과 같다고 해보자.서점에서 책을 샀다.돈을 인출하고 가는길에 아이스크림을 사먹고, 책을 구매했다.추상화된 내용을 보고 구체적인 내용의 유추가 어렵다. 의미를 담을 수 있는 더 작은 단위로 쪼개야 한다.현금 인출아이스크림 사먹기서점에서 책 구입하기메서드 작성메서드 선언부반환타입메서드명파라미터메서드명추상화된 구체를 유추할 수 있는, 적절한 의미가 담긴 이름파라미터와 연결지어 더 풍부한 의미를 전달할 수도 있다.파라미터파라미터의 타입, 개수, 순서를 통해 의미를 전달파라미터는 외부 세계와 소통하는 창어떤 재료가 필요한지 알려주는 역할이다. 외부 세계한테 필요한 재료를 요구한다.반환타입메서드 시그니처에 납득이 가는, 적절한 타입의 반환값 돌려주기반환 타입이 boolean인데, 이게 이 메서드에서 무엇을 의미하는거지?void 대신 충분히 반환할 만한 값이 있는지 고민해보기반환값이 있다면 테스트도 용이해진다.코드의 줄 수가 많아서 추상화하는 것이 아니다! 같은 라인 수를 가지더라도 추상화할 수 있다.추상화 레벨메서드를 추출한다 ⇒ 외부 세계와 내부 세계를 나누고, 추상화 레벨이 나뉜다.하나의 세계 안에서는, 추상화 레벨이 동등해야 한다!게임 시작 멘트 출력게임 초기화게임 보드 보여주기게임 상태가 1이면게임 종료 메시지 출력1, 2, 3, 4를 쭉 읽다보면 4에서 멈칫하게 된다. 게임 상태 = 1이라는 것의 의미에 대해서 의문이 생기게 된다. 즉, 읽는 사람이 해석을 하게 만든다. 따라서 동등한 추상화 레벨을 맞춰야 한다.게임 시작 멘트 출력게임 초기화게임 보드 보여주기게임을 이겼다면,게임 종료 메시지 출력전보다 훨씬 읽기 수월하다.메서드로 추출한다는 것은 로직이 복잡하거나 의미를 부여할 수 있어서도 맞지만, 추상화 레벨을 동등하게 맞춤으로써 읽는 사람으로 하여금 자연스럽게 이해할 수 있게 한다.매직 넘버, 매직 스트링의미를 갖고 있으나, 상수로 추출되지 않은 숫자, 문자열 등상수 추출로 이름을 짓고 의미를 부여함으로써 가독성, 유지보수성 증가섹션 3. 논리, 사고의 흐름인지적 경제성코드를 작성할 때 인지적 경제성을 추구하도록 작성해보자!💡 인지적 경제성? - 최소의 인지적 노력으로 최대의 정보 제공 - 한 번에 한 가지 일 즉, 최소한의 인지만 가져가서 최대의 효율을 내보자라는 뜻!코드를 읽는 사람의 메모리를 효과적으로 쓸 수 있도록 해서 읽기 좋은 코드를 작성하자.Early Returnelse if ⇒ 이전의 if에 대해서 생각한다.else ⇒ 앞의 조건들을 모두 알아야 안다.즉, 앞 선 정보들을 모두 기억하고 있어야 함!Early Return을 이용하자!else가 사라짐앞의 코드를 신경 쓸 필요가 없다.사고의 depth 줄이기중첩 분기문, 중첩 반복문2중 for문 내에 if 문이 있다면?3 depth ⇒ 사고의 depth도 많을 것!메서드 추출을 통해 중첩 분기문/반복문을 없애자! 그렇다고 무조건 1 depth로 만들라는 것은 아니다.추상화를 통한 사고 과정의 depth를 줄이는 것이 중요2중 중첩 구조로 표현하는 것이 사고하는 데 더 도움이 된다면 그대로 놔두자.사용할 변수는 가깝게 선언하기사용할 변수는 가깝게 선언하기사용할 변수가 너무 멀리있다면 되돌아가도록 유도한다.공백 라인도 의미를 가진다!복잡한 로직의 의미 단위를 나누어 보여줌으로써 읽는 사람에게 추가적인 정보를 전달할 수 있다.우리나라 글도 문단이 있지 않은가!부정어를 대하는 자세!가 있으면 조건을 먼저 이해하고, 이에 반대되는 조건을 생각하게 된다.가독성이 떨어지고 비틀어서 생각하게 함읽으면서 바로 생각할 수 있게 조건을 작성하자.해피 케이스와 예외 처리예외가 발생할 가능성 낮추기검증이 필요한 부분 = 외부 세계와의 접점의도한 예외와 예상하지 못한 예외 구분사용자에게 보여줄 예외개발자가 보고 처리해야 할 예외항상 NPE가 일어나지 않는지 고려하자.메서드 설계 시 return null 자제Optional은 비싼 객체이고 반환 타입에 사용한다.orElse, orElseGet, orElseThrowe.printStackTrace는 실무에서는 안티패턴이다!섹션 4. 객체 지향 패러다임추상의 관점으로 바라보는 객체 지향절차 지향정해진 순서 차례대로 처리하는 프로그래밍객체 지향객체를 만들어서 객체들 간의 협력을 통해 이루어지는 프로그래밍함수형순수 함수를 정의Side Effect가 없는 함수A ⇒ 항상 A에 대한 정해진 결과가 나오는 것객체란?추상화된 데이터 + 코드객체간의 협력과 객체가 담당하는 책임객체 지향의 특징 ⇒ 캡슐화, 추상화, 상속, 다형성정말 이를 이해하고 적용하고 있는지 스스로 질문해보자!코드 레벨에서 잘 녹여서 쓸 수 있어야 한다.관심사의 분리유지보수가 원활해짐높은 응집도와 낮은 결합도같은 관심사는 응집도가 높아야 한다.각 관심사끼리는 결합도가 낮아야 한다.객체 설계객체로 추상화는..비공개 필드(데이터), 비공개 로직(코드)공개 메서드 선언부를 통해 외부 세계와 소통객체의 책임이 나뉨에 따라 객체 간 협력 발생!객체를 통해절차 지향에서 보이지 않았던 개념 가시화관심사가 한 군데로 모이기 때문에 유지보수성 증가객체를 사용하는 입장에서 보다 높은 추상화 레벨에서 도메인 로직을 다룰 수 있다!!주의사항1개의 관심사로 책임이 분리되었는지 스스로 질문해보자.객체 생성 시, 유효성 검증이 가능setter는 지양, 필요할 경우 메서드명에 의도를 드러내자.getter는 필요한 경우에만.필드 수는 적을 수록 좋다.불필요한 데이터를 굳이 가지고 있지 말자. ex) 주문의 총 가격도메인 지식은 만드는 것이 아니라 발견하는 것!SRP (단일 책임 원칙)하나의 클래스가 하나의 책임만 갖도록 설계해라!하나의 클래스는 한 가지의 변경 이유만 가진다.객체가 가진 공개 메서드, 필드, 상수 등은 해당 객체의 단일 책임에 의해서만 변경 되는지 질문하자.변경이유 = 책임관심사의 분리높은 응집도, 낮은 결합도응집도 = 클래스나 모듈 내 요소들이 서로 긴밀하게 연관되어 있는 정도결합도 = 두 개 이상의 객체가 협력할 때, 한 객체가 변경되었을 때 다른 객체가 영향받는 정도각 객체가 독립적인 책임을 가지도록 잘 쪼개진다.객체를 하나의 변경 지점, 하나의 책임으로만 가지도록 설계하자!책임을 발견하기 어려움경계선이 사람마다, 도메인마다 다를 수 있음설계한 객체가 하나의 책임만 가지고 있는지 끊임없이 질문책임을 보는 눈을 기르자!객체를 설계했을 때 책임이 잘 응집되었는가?, 단일 책임만을 가지고 있는가?OCP (개방-폐쇄 원칙)확장에는 열려 있고, 수정에는 닫혀 있어야 한다.기존 코드 변경 없이 기능 확장이 가능해야 함!추상화와 다형성을 활용하여 OCP를 지키자.새로운 요구사항이 추가되었을 때, 기존 코드가 과도하게 변화된다?OCP를 지키지 못하고 있는게 아닌지 생각하자.LSP (리스코프 치환 원칙)상속 구조에서, 부모 클래스의 인스턴스를 자식 클래스의 인스턴스로 치환할 수 있어야 한다.자식 클래스는 부모 클래스의 책임을 준수하고 행동을 변경하지 않아야 한다.부모가 일하는 곳에 자식이 가더라도 문제가 없어야 한다!LSP 위반상속 클래스를 사용할 때 오동작 발생ISP (인터페이스 분리 원칙)클라이언트는 자신이 사용하지 않는 인터페이스에 의존하면 안 된다.기능 단위로 인터페이스를 잘게 쪼개라!내가 구현하려는 인터페이스의 기능 명세에 사용하지 않는 기능이 있다.자신이 사용하지 않는 인터페이스에 의존하게 됨불필요한 의존성으로 인해 결합도가 높아진다.특정 기능의 변경이 여러 클래스에 영향을 미친다.DIP (의존성 역전 원칙)상위 수준의 모듈은 하위 수준의 모듈에 의존해서는 안 된다.둘 모두 추상화에 의존해야 한다.의존성의 순방향 : 고수준 모듈이 저수준 모듈을 참조하는 것의존성의 역방향 : 고수준, 저수준 모듈이 모두 추상화에 의존하는 것저수준 모듈이 변경되어도, 고수준 모듈에는 영향이 가지 않는다.기능을 추상화하여 고수준 모듈은 추상화된 스펙만 참조추상화를 중간에 두고 의존하도록 하자!저수준 모듈이 자유롭게 변경되도 고수준 모듈에는 영향 X💡스프링에서의 DI, IoC DI = Dependency Injection - 필요한 의존성을 직접 생성하는 것이 아니라, 외부에서 주입받는다. - 제 3자(스프링 컨텍스트)가 런타임 시점에 의존성을 주입해준다. IoC = Inversion of Control - 프로그램의 흐름을 프레임워크가 담당 - 빈 생성, 의존성 주입, 생명주기 관리를 스프링 컨테이너가 해준다. 섹션 5. 객체 지향 적용하기상속과 조합상속보다 조합을 사용하자.상속은 시멘트처럼 굳어지는 구조수정이 어렵다.부모와 자식의 결합도가 높다.자식이 부모에 대해서 다 알고 있어야 함부모 수정 ⇒ 자식이 다 영향을 받는다.자식은 부모에 있는 필드를 직접적으로 알고 있음조합과 인터페이스를 활용하는 것이 유연한 구조상속을 통한 코드의 중복 제거가 주는 이점보다, 중복이 생기더라도 유연한 구조 설계가 주는 이점이 더 크다.Value Object도메인의 어떤 개념을 추상화하여 표현한 값 객체값으로 취급하기 위해 불변성 동등성 유효성 검증 등을 보장해야 한다.불변성 : final 필드, setter 금지동등성 : 다른 인스턴스여도 내부 값이 같으면 같은 값 객체로 취급유효성 검증 : 객체가 생성되는 시점에 값에 대한 유효성 보장VO vs EntityEntity식별자 O식별자가 아닌 필드의 값이 달라도, 식별자가 같으면 동등한 객체로 취급!equals & hashcode도 식별자 필드를 통해 재정의VO식별자 X내부의 모든 값이 다 같아야 동등한 객체일급 컬렉션일급 시민다른 요소에게 사용 가능한 모든 연산을 지원하는 요소변수로 할당될 수 있다.파라미터로 전달될 수 있다.함수의 결과로 반환될 수 있다.일급 함수함수형 프로그래밍에서 함수는 일급 시민함수는 변수에 할당될 수 있고, 인자로 전달될 수 있고, 함수의 결과로 함수가 반환될 수도 있다.일급 컬렉션컬렉션을 포장하면서, 컬렉션만을 유일하게 필드로 가지는 객체컬렉션을 다른 객체와 동등한 레벨로 다루기 위함단 하나의 컬렉션 필드만을 가진다.컬렉션을 추상화하며 의미를 담고, 가공 로직의 보금자리가 생긴다.가공 로직에 대한 테스트도 작성 가능getter로 컬렉션을 반환할 일이 생긴다면, 외부 조작을 피하기 위해 새로운 컬렉션을 만들어 반환해야 한다.Enum의 특성과 활용enum은 상수의 집합상수와 관련된 로직을 담을 수 있음특정 도메인 개념에 대해 종류와 기능 명시변경이 잦은 개념은 DB로 하는게 나을 수도 있다.enum은 코드 ⇒ 배포를 해야만 변경할 수 있다.변경이 잦은 요소라면 DB로 관리하자.숨겨져 있는 도메인 개념 도출하기도메인 지식은 만드는 것이 아니라 발견하는 것객체 지향은 현실을 100% 반영하는 도구가 아니라, 흉내내는 것현실 세계에서 쉽게 인지하지 못하는 개념도 도출해서 사용해야 할 때가 있다.설계할 때는 근시적, 거시적 관점에서 최대한 미래를 예측하고, 시간이 지나 틀렸다는 것을 인지하면 언제든 ㄷ돌아올 수 있도록 코드를 만들어야 한다.완벽한 설계 X, 그 당시의 최선이 있을 뿐 📌 1주차 수강 후 느낀점강의 제목들을 보면, 사실 개발자라면 다 한번씩 들어봤을 내용이다. SOLID 원칙, 추상화, VO, Enum 등등 누군가 질문하면 그럴듯한 답변을 할 수 있는 주제다. 그런데 ‘이 내용들을 깊이 있게 이해하고 있을까?’ 라는 질문에는 아니었던 것 같다.이러한 개념들을 직접 코드 레벨에 적용하며 개선하는 과정에서 배울 점이 많았다. 내가 알고 있던 깊이는 상당히 얄팍했다. 가끔 헷갈리면 검색해서 ‘아 대충 이런내용이었지.’하고 금방 닫곤 했는데, 이번 기회에 직접 경험해보며 내 지식들을 구체화시킬 수 있었다. 개발자가 가볍게 여기고 넘어갈 수 있는 주제들에 대해 다시 생각하며, 기본기의 중요성을 알 수 있었다! 그리고, 기존에 프로젝트하며 고민했던 지점이 있었다. 추상화를 할 때 ‘같은 라인 수를 가져도 굳이 해아하나?’ 생각했었는데, 나한테 해답이 되는 1주차였던 것 같다!! 전부터 ‘한 번 들어야지!’했던 강의인데 워밍업 스터디를 계기로 수강하길 잘한 것 같다🙂 📌 1주차 미션Day 2 : 추상과 구체 예시 생각해보기뭔가 추상화를 실생활에 생각해본 적은 없었는데, 느낌이 새로웠다. 되게 개발스러운 단어라고만 생각했는데 추상은 우리의 삶에도 가까이 적용되는 단어라는 것을 느꼈다.농구에서 슛을 한다고 해보자. 이때 무릎을 구부리고, 팔꿈치를 올리고, 무릎을 다시 피고, 팔꿈치를 다시 피고, 손목을 피며 공을 던진다. 위 여러 과정들이 ‘슛’으로 추상화된다. 우리 삶의 추상화는 상당히 자주 쓰인다!Day 4 : [섹션 3. 논리, 사고의 흐름] 내용을 중심으로 리팩토링하기이전에는 코드를 리팩토링하면, 되게 느낌적으로 다가갔던 것 같다. ‘뭔가 이게 나은 것 같긴한데..’하며 접근하거나 단순히 코드 라인을 줄이는 등 이유와 근거가 빈약했다. 하지만 Day 4 미션을 통해 리팩토링의 이유와 근거를 통해 리팩토링을 진행해보았다.필요한 정보를 빼내 추상화부정어구 최소화케이스 확인위 과정을 통해 미션에서 주어진 코드를 읽기 좋게 개선하였다. 읽으면서 사고의 흐름이 멈칫하거나 꼬일 수 있던 코드를 바로 읽어나갈 수 있게 작성하려고 노력했다.강의를 들으면서 내가 납득됐던 내용에 대해 직접 리팩토링에 적용하여 ‘읽기 좋은 코드’를 만들어 나갈 수 있었다. 단순히 불필요한 작업을 없애는 것만이 리팩토링이 아니라, ‘읽기 좋은 코드’를 만드는 것도 중요한 리팩토링이라는 것을 깨달았다!

백엔드추상객체지향

10

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

🤔 인프런 워밍업 클럽 스터디 신청 계기나는 인프런 워밍업 클럽 스터디 신청하기 한 달 전에 스터디 과정에 쓰이는 로드맵 강의를 이미 완강하였다.그럼에도 불구하고 강의를 수강하기 전부터 우빈님의 팬(?)이였기 때문에 이메일을 받자마자 신청을 할 수 밖에 없었다.항해 플러스 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://github.com/discphy/warm-up-backend-code/pull/11⃣ 추상과 구체 예시강의 내용에서도 계속 등장하듯이 추상과 구체는 이번 강의에서 가장 핵심이 되는 단어들이다.이 미션을 접했을 때, 단순히 추상과 구체에 대한 예시를 들기보다 개발적인 관점에서 추상과 구체에 대해 접근하려고 노력했다. (적절한 예시인지는 잘 모르겠으나..😂)해당 미션을 통해, 추상화가 단순히 쉽지 않다는 걸 느꼈으며 개발적인 관점에서도 더 읽기좋은 코드를 작성하기 위해선 추상화를 잘 해야겠다라는 생각이 들었다. [미션 Day 4]미션 PR : https://github.com/discphy/warm-up-backend-code/pull/21⃣ 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://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 14일 전
예은

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

김민우 튜터님의 <시작하는 PM/PO들에게 알려주고 싶은, 프로덕트의 모든 것>을 수강하며 인프런 워밍업 클럽에 참여하고 있다.2기에 이어 3기에도 참여 중인데, 이번에도 완주까지 힘내보자.배움프로덕트 매니저란비즈니스에서 필요한 성과를 이해하고 회사의 전략에 맞춰 방향성을 제시하는 사람고객에게도 가치를 제공하고, 우리 사업에도 도움이 되는 프로덕트를 만드는 사람   2. 성공적인 프로덕트의 조건ValuableUsableReasibleViable 3. 프로덕트 마켓 핏(PMF)을 찾은 이후 제품 조직이 하는 일Feature WorkGrowth WorkPMF ExpansionScaling Work (주로 프로덕트 리더가 신경 씀)4. 문제 정의 문제를 종•횡으로 오르내리며 Problem Space, Solution Space를 충분히 탐색할 것우리가 정의한 문제는 의식하지 못한 상위 문제의 솔루션을 내포할 수 있음그 문제는 어떤 점 때문에 중요한가?그 문제는 정말로 중요한가? (기회비용 관점에서 사고)현상 아래 원인은 무엇인가?  5. 이터레이션(Iteration) 제품 제작 -> 테스트 -> 학습 -> … 반복하며 발전피드백 수집 및 개선  6. 어떤 문제가 해결할 가치 있는 문제일까?사람들이 적극적으로 해결책을 찾아 보았는가? (불편하다, 그런 제품이 있으면 이용하겠다 X) 사람들이 문제 해결을 위해 시간과 돈을 쓰는가? 타당한 비즈니스 모델을 만들 수 있는 문제회고스스로 칭찬하고 싶은 점개인적으로 새로운 환경에 적응하느라 공부를 병행하는 게 조금 어려웠지만 어떻게든 해냈다. 아쉬웠던 점아직까지 학습이 습관화되지 않은 것 같다. 개선이 필요하다보완하고 싶은 점계획을 세워 여유롭게 학습할 수 있는 환경을 조성하고 싶다.다음주에는나만의 규칙을 만들어 일정한 시간에 학습할 것이다.단순히 멘토님의 강의만을 활용하는 것에서 더 나아가, 스스로 찾아보고 공부하며 깊이 있게 학습할 것이다. 

기획 · PM· POPMPO기획프로덕트

제갈진우

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

1주차 학습 회고: PM으로서 문제 정의와 해결의 중요성이번 1주차 강의를 통해 PM의 핵심 역할을 다시금 정리할 수 있는 기회가 되었습니다. PM은 고객과 비즈니스의 가치를 균형 있게 만들어가는 역할이며, 이를 위해 Valuable(가치 있는), Usable(사용 가능한), Feasible(개발 가능한), Viable(사업적으로 지속 가능한) 제품을 만들어야 한다는 것이 다시 한번 강조되었습니다.특히 문제 정의의 중요성을 깊이 실감했습니다. PM은 단순히 문제를 해결하는 것이 아니라, 해결할 가치가 있는 문제를 찾는 것이 핵심 역할입니다. 이 과정에서 "문제 공간을 2차원으로 시각화하고 Deep Dive하는 방법"과 "Feature Work와 Growth Work를 구분하여 접근하는 방식"이 인상적이었습니다. 기존에도 문제를 정의하려고 노력했지만, 실제로 이를 구조화하는 데 어려움이 있었는데, 이번 강의를 통해 좀 더 명확한 기준을 가질 수 있을 것 같습니다.또한, "모든 고객의 문제가 비즈니스로 연결되지는 않는다"는 점이 다시금 와닿았습니다. 문제를 정의하고 나면, 본능적으로 해결책을 고민하게 되는데, 정작 이 문제가 해결할 가치가 있는지에 대한 검증을 소홀히 할 때가 많습니다. 앞으로는 데이터 기반으로 문제를 분석하고, 해결책을 검증하며 이터레이션을 반복하는 과정을 더 의식적으로 적용해야겠다고 다짐했습니다. 강의를 들으면서 가장 와닿았던 부분은 "학습이 끝이 아니라 실천해야 한다"는 점입니다. 결국 이 내용을 업무와 실제 프로젝트에 적용해보면서 나만의 방법론으로 체득하는 것이 중요합니다. 사이드 프로젝트에서도 이번 강의 내용을 활용해 문제 정의를 더욱 명확히 하고, 분석 툴을 적극적으로 활용해볼 계획입니다. 마지막으로, 학습한 내용을 단순히 듣고 끝내는 것이 아니라, 직접 회고를 작성하면서 다시 한번 정리하는 것이 도움이 되었습니다. 앞으로도 학습한 내용을 적용하고 기록하는 습관을 들여야겠다고 생각합니다.

기획 · PM· PO워밍업클럽pmpo데이터분석가1주차회고

천준민

워밍업 클럽 3기 BE 클린코드&테스트 - 1주차 발자국

📖 강의 요약 2⃣ 추상우리가 클린 코드를 추구하는 이유"가독성"혼자만 사용한다면 아마 필요 없는 이야기 일수도 있을 것이다 하지만 개발자는 협업이 중요하다고 들었다.나만 코드를 읽는 것이 아니라 협업을 하는 사람들끼리 컨벤션도 중요하지만 그 전에 가독성 있는 코드를 짜면 원활한 작업을 할수 있을 것이라고 생각한다. 추후 발생하는 비용들도 줄어들 것이다.이번 섹션에서 강조하는 것은 제목처럼 추상인 것 같다.추상을 알아보자"추상"중요한 정보는 가려내어 남기고 덜 중요한 정보는 생략하여 버린다.미션에서 제출한 내가 이해한 추상과 구체를 위와 같이 예시를 들수 있을 것 같다.[추상] 인프런 워밍업 클럽 3기 BE 클린코드 & 테스트를 수료 한다. [구체] 2 개 강의(Readable Code: 읽기 좋은 코드를 작성하는 사고법&Practical Testing: 실용적인 테스트 가이드 )를 100 % 수강한다. 주 1회(총 4회) 발자국 작성을 한다. OT를 포함한 총 3회 온라인 라이브를 출석한다 미션 6개 중 5개 이상 기한 내 제출 완료한다.추상화의 가장 대표적인 행위는 이름 짓기,메서드 선언, 동등한 추상화 레벨, 매직 넘버,매직 스트링 제거 등 이 있다.위 주제에서 공통적으로 이름을 잘 짓는 것이 기반이 되는 것 같다. ex) 변수이름, 메서드 이름, 상수 이름 3⃣ 논리, 사고의 흐름이번 섹션에서 한마디로 정리하자면 빼고 생각하자 였다. (로직이 복잡하면 그 내가 먼저 생각 할 수 있는 상황부터 먼저 거르고 생각하기)추가적으로 부정어, 예외처리, null,optional에 대해 한번 더 생각해보고 작성해보기 4⃣ 객체 지향 패러다임이번 섹션은 좀 반성을 좀 하였다. 특히 강의 중 getter 사용 에 대한 이야기 였는데 무조건 getter를 사용하면 객체안에 정보를 가져올수 있잖아라고 생각하는 나에게 생각을 많이하게 되었다.핵심 : 관심사를 분리, 높은 응집도, 낮은 결합도, getter를 사용하기 전에 객체의 메시지를 보내서 그 값을 사용할수 있는 지 부터 생각하는 능력 기르기 SOLID 원칙✅SRP : 하나의 클래스는 단 한 가지 변경 이유(= 책임) 만을 가져야 한다.✅OCP: 확장에는 열려 있고, 수정에는 닫혀 있어야 한다.✅LSP: 자식 클래스는 부모클래스의 책임을 준수하며, 부모 클래스의 행동을 변경하지 않아야 한다.✅ISP : 클라이언트는 자신이 사용하지 않는 인터페이스에 의존하면 안된다.✅DIP : 상위 수준의 모듈은 하위 수준의 모듈에 의존해서는 안된다. 5⃣ 객체 지향 적용하기이번 섹션은 내가 잘 이해를 잘못한 것 같아 다시 보아야 할 것 같다. 지금은 일기 정도로 작성 하지만 이해하여 꼭 적어보자강의를 듣고 옛날에 내가 작성한 코드(전체 코드 x)가 생각 났다.public enum ReservationStatus { PENDING("PENDING") { @Override public boolean canChangeTo(ReservationStatus status) { switch (status) { case APPROVED, CANCELED, EXPIRED -> { return true; } } return false; } }, APPROVED("APPROVED") { @Override public boolean canChangeTo(ReservationStatus status) { return status.equals(APPROVED); } }, CANCELED("CANCELED") { @Override public boolean canChangeTo(ReservationStatus status) { return status.equals(CANCELED); } }, EXPIRED("EXPIRED") { @Override public boolean canChangeTo(ReservationStatus status) { return status.equals(EXPIRED); } };지금 보니 메서드 명도 바꿀 필요가 있고 switch 사용으로 유지보수 어려움, if-else 사용 등 고쳐야할 부분이 많은 것 같다.목표 : 강의를 어느 정도 이해하여 코드를 바꿔보기 💡 미션 코드와 설명을 보고, [섹션 3. 논리 의 사고 흐름]에서 이야기 하는 내용을 중심으로 읽기좋은 코드로 리팩토링 해보기 블로그: https://zunmin4030.tistory.com/56 먼저 사이즈가 0이면 어떤 행위를 하고 0보다 크면 어떤 행위 0보다 작으면 어떤 행위를 한다라는 접근으로 코드를 이해 하는 것 부터 시작하였다. 거기에 대한 궁금증을 가지고 섹션별 내가 적용할 수 있는 부분을 생각해보았다. 이름짓기 , 부정 연산자 제거, Early return을 생각하여 과제를 해결하다보니 처음 코드를 말로 정리했을 때 생긴 궁금증을 해소 할수 있었다. 💬 회고 👍 잘한 점배운 것을 하나씩 적용해 보며 과제를 수행 해 나갔다.😮‍💨 아쉬웠거나 보완하고 싶은 점섹션 5에 대한 이해를 하지 못해 한번 더 공부 해야겠다. 📎출처Readable 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

백엔드인프런워밍업클럽backend3기

강동훈

[인프런 워밍업 클럽 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기

szun

워밍업 클럽 스터디 3기(PM) 1주차 발자국

강의명: 시작하는 PM/PO들에게 알려주고 싶은, 프로덕트의 모든 것코치: 김민우링크: https://www.inflearn.com/course/%EC%8B%9C%EC%9E%91%ED%95%98%EB%8A%94-pmpo-%EB%AA%A8%EB%93%A0%EA%B2%83/dashboard1주차 강의 포인트PM의 역할은 제품으로써 고객 가치와 사업적 가치를 만들어 내는 것여기서 제품이 '성공적인 프로덕트'이기 위해서는 4가지 조건이 필요함.ValuableUsableFeasibleViablePM은 주로 Valuable과 Viable을 담당하는 역할임PM은 고객, 데이터, 비즈니스, 산업에 대한 심층적인 지식을 가지고 있어야 함.결국 중요한 것은 PMF(Product/Market Fit)을 찾는 것!문제를 잘 정의해야 해결 할 수 있다.문제를 잘 정의 했다면 이제는 해결책을 만들 차례, 좋은 해결책을 내기 위해서는 다음 두 가지가 필요좋은 아이디어이터레이션(Iteration): 만들고, 테스트하고, 학습하기를 반복하여 제품을 발전시키는 것하지만 모든 고객 문제들이 비즈니스로 연결되지는 않는다는 것을 명심해야 함.우리는 Problem Worth Solving, 즉 해결할 가치가 있는 문제를 찾아서 해결해야 함. 1주차 회고스타트업을 창업하면서 항상 중요하다고 인식을 해온 개념이었지만, 과연 내가 실천하고 있었을까 돌아보는 시간이 되었다. 특히 문제 정의 부분과 나의 문제 assumption이 해결할 가치가 문제가 맞는지를 판단하는데에 막연함이 있었는데, 코치님께서 알려주신 해당 상황에서의 판단 기준은 앞으로 나의 업무에 있어 큰 도움이 될 것 같은 기준들이었다. 강의를 들으면서도 결국 중요한 것은 내가 직접 실천하여 나의 것으로 만드는 과정이라는 생각이 들었다. 업무를 할 때에 있어 의식적으로 생각의 방향을 잘 정립하여 수행해보고자 한다. 

기획 · PM· PO

윤기숙

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

📖 1주차 - 학습 내용 정리1. 피그마 배리어블과 디자인 토큰 개념 정리피그마 배리어블과 디자인 토큰의 개념 다시 한번 정리하기 Semantic이 중요하게 필요한 이유 Mode로 구분 가능하고, 로컬 베리어블 관리가 필요하기 때문2. 배리어블과 파운데이션 세팅하기색상, 타이포그래피, 간격, 선, 그림자, 그리드 등 베리어블 직접 등록해보기다양한 효과(Effect) 알아보기, Drop Shadow외에도 다른 효과들도 사용해보기높낮이(Elevation)는 UI 요소 간의 계층 구조를 시각적으로 명확하게 나타내고, 베리어블로 정리하면 유용그리드 종류 Fixed Grid(centered), Fluid Grid(stretch), Hybrid Grid 살펴보기.디바이스별 반응형 디자인 기준점(viewport) 정리해보고, Min width와 Max width를 지정해주기.3. 유용한 피그마 플러그인들 적용해보기  💡 1주차 - 회고 및 느낀 점 😆 좋았던 점강의를 듣고 직접 실습하면서 배리어블 개념, 용어, 피그마 플러그인등을 정리할 수 있었던 점이 좋았다.UI3에 맞춰서 업데이트 된 강의를 들을 수 있는게 너무 좋았다. 이전에 강의 들을 당시 예전 UI와 다른점이 많아서 이해하기 어려운 부분들도 있었는데, 이번에 다시 정리하면서 들으니 너무 좋았고, 좀더 체계적으로 추가 업데이트 된 내용을 배울 수 있어서 좋았다.😅 아쉬웠던 점스터디 첫 주였는데 계획했던 것처럼 매일 강의를 듣지는 못했고, 정해진 시간에 아직 루틴화 되지 못한 점, 강의 따라가기에 바빠서 스타일가이드 문서화 정리까지는 제대로 못했던 점이 아쉽다.😍 앞으로 바라는 점다음 주부터는 정해진 시간에 조금씩이라도 매일 공부하고, 문서화 하는 부분도 염두해두면서 작업해보자 💪 

UX/UI인프런_워밍업_클럽디자인시스템FigmaUX/UI

최범수

CQRS와 조회 전용 저장소, 그리고 카테고리-상품 관계 고민기

도메인 주도 설계를 배우면서 겪은 고민최근 도메인 주도 개발(DDD)에 관한 책을 읽으면서 여러 가지 설계에 대한 깊은 고민을 하게 되었다. 특히 관심을 끈 부분은 CQRS(Command Query Responsibility Segregation) 개념이었다. 이 글에서는 공부한 CQRS 개념과 함께, 카테고리와 상품 사이의 관계를 어떻게 효율적으로 설계할 수 있는지 정리해 보려고 한다. 쓰기 모델과 조회 모델을 분리한 이유일반적인 웹 애플리케이션은 데이터베이스 하나에서 읽기 쓰기를 모두 처리한다. 이렇게 구현해도 초반에는 큰 문제없이 잘 동작하지만, 서비스가 성장하면서 읽기(조회) 트래픽이 급증하면 다음과 같은 문제가 발생할 수 있다.읽기 요청 증가로 인한 데이터베이스 성능 저하복잡한 검색 및 필터링으로 인한 성능 한계이를 해결하기 위한 방법 중 하나가 CQRS 이다. CQRS의 핵심 개념쓰기(명령)와 읽기(쿼리)의 책임을 분리쓰기 작업은 RDB에 반영하고, 읽기는 ElasticSearch나 Redis 같은 빠른 저장소를 활용CQRS 흐름쓰기 발생: 데이터를 RDB에 저장이벤트 또는 동기화: Kafka 등을 통해 조회 전용 저장소(ElasticSearch)에 데이터 동기화조회 요청 처리: 조회 전용 저장소에서 빠르게 검색/조회이를 통해 조회 성능 향상과 시스템 부하 분산을 얻을 수 있다. 하지만 이벤트 동기화 과정에서 구현 복잡도가 늘어나고, 실시간성이 필요한 경우 동기화 지연이 단점으로 작용할 수 있다.간단 예시: CQRS 적용 시나리오 상품 생성 시:상품 정보를 RDB에 저장이벤트를 발생 (예: "상품 생성됨")이벤트 소비자가 ElasticSearch에 해당 상품 정보를 인덱싱사용자 조회 시:ElasticSearch에서 상품 목록 검색 -> 빠른 검색 결과 반환카테고리와 상품 관계 설계카테고리와 상품 간의 설계는 애플리케이션 성능, 특히 페이징 처리와 깊은 관련이 있다.데이터 조회와 성능에 큰 영향을 미치는 두 가지 대표적인 설계 방식을 자세히 살펴보자. N-1 관계 (상품 -> 카테고리 참조)N-1 관계는 상품(Product)이 특정 카테고리(Category)를 직접 참조하는 방식이다. 일반적으로 상품이 반드시 하나의 카테고리에만 속할 때 이 방식을 사용한다.@Entity public class Product { @Id private Long id; @ManyToOne @JoinColumn(name = "category_id") private Category category; }장점특정 카테고리에 속한 상품을 효율적으로 페이징 가능하다.category_id컬럼에 DB 인덱스를 설정하면 빠른 조회가 가능하다.단점카테고리에서 직접적으로 상품 목록에 접근하는 것이 어렵다.N+1 문제가 발생할 가능성이 높다.이 문제는 JPA의 Fetch Join이나 EntityGraph 같은 기법으로 해결할 수는 있으나 본 글의 내용과는 관련성이 떨어지기 때문에 자세히 다루진 않도록 하겠다.  M-N 관계 (상품 <-> 카테고리 ID로 연결)M-N 방식은 하나의 상품이 여러 개의 카테고리에 속할 때 사용하는 구조로, 중간 테이블을 사용하여 다대다 관계를 처리한다. 특히 ID기반으로 연관관계를 표현하여 직접적인 엔티티 참조를 피하는 방식이다.@Entity public class Product { @Id private Long id; @ElementCollection @CollectionTable(name = "product_category", joinColumns = @JoinColumn(name = "product_id")) @Column(name = "category_id") private Set<Long> categoryIds; }장점엔티티 간 직접적인 양방향 참조 없이 ID로만 관계를 맺어 결합도를 낮춘다.데이터가 많은 때도 페이징 및 필터링을 DB에서 효과적으로 처리할 수 있다.단점특정 카테고리에 속한 상품을 조회하려면 추가적인 쿼리가 필요하다.카테고리와 상품 정보를 함께 조회할 경우 성능 저하가 발생할 수 있다. 어떤 방식을 선택해야 할까?관계를 설계할 때는 애플리케이션의 사용 패턴과 비즈니스 요구사항을 깊게 고민해야 한다. 각각의 관계 방식은 다음과 같은 상황에 효과적이다.N-1 방식상품이 주로 하나의 카테고리에 속하는 경우상품 중심으로 자주 조회하는 기능이 필요할 때조회 성능 및 빠른 페이징이 중요한 경우M-N 방식하나의 상품이 여러 카테고리에 동시에 속할 수 있는 경우다양한 조건으로 필터링하거나 복잡한 조회가 필요한 경우많은 데이터를 효율적으로 처리해야 할 경우 글을 마무리하며이 글을 작성하면서 설계 방식 하나를 선택하는 일이 얼마나 중요한 고민인지 새삼 느끼게 되었다.이론적으로는 두 방식 모두 장단점이 있기 때문에 어느 하나가 더 우월하다 말할 수는 없겠지만, 결국 실제 서비스에서 어떤 방식이 더 효율적일지는 요구사항을 고려하여 더 효율적인 선택을 하는건 개발자의 몫인 것 같다.앞으로도 단순히 이론에 그치지 않고 실무에서 끊임없이 고민하며 상황에 맞는 좋은 설계 방식을 찾아가야겠다는 생각이 들었으며 끝으로 이 글의 내용과 비슷한 고민을 하고 있는 사람들에게 조금이나마 도움이 되었기를 바란다.

백엔드CQRSManyToOneManyToMany조회전용저장소

Pleos25

[현대자동차그룹] 개발자 컨퍼런스 'Pleos 25' 참가자 모집 (~3/27)

개발자들을 위한 새로운 무대, Pleos 25지난 4년간 개발자들과 모빌리티 생태계를 만들어온 HMG developer conference가현대자동차그룹의 통합 소프트웨어 브랜드 'Pleos'와 함께 완전히 새로워진 모습으로 찾아왔습니다.​🧐Pleos가 무엇인가요?Pleos는 HMG의 소프트웨어 역량을 하나로 모은 그룹사 통합 소프트웨어 브랜드입니다.📍Pleos 25는 어떻게 진행되나요? 이번 행사 현장에 오시면 SDV, AI, 자율주행 등 다양한 주제의 트랙 세션을 들으실 수 있고, 차량용 앱 개발 및 SDK 활용 실습을 할 수 있는 핸즈온 세션에 참여하실 수 있습니다!관심있는 개발자 분들 모두 참가하실 수 있으니 많은 관심 부탁드리며 현대자동차그룹 전문가들과 함께 개발 경험과 지식을 나누어 성장하는 기회가 되셨으면 좋겠습니다.다가오는 3월 28일, 코엑스에서 열릴 'Pleos 25'에서 만나요!▪일시 : 2025년 3월 28일 (금) 오전 10:00▪장소 : 코엑스 오디토리움 & 전시홀 D▪참가 대상 : 차량용 앱 생태계에 관심있는 개발자 및 비즈니스 파트너▪행사 구성- Track : SDV, AI, 자율주행 등 다양한 분야의 개발자들이 전하는 생생한 개발 경험과 인사이트를 공유합니다.- Exhibition : 차량 데이터를 활용한 현대자동차그룹과 파트너사들의 다양한 협업 사례와 모빌리티 최신 기술 및 솔루션을 만날 수 있습니다.- Hands-on : 차량용 앱 개발 및 SDK 활용 방법에 대해 직접 체험해 볼 수 있습니다.- 채용 상담 : 현대자동차그룹에서 제공하는 다양한 분야의 채용 상담도 준비되어 있습니다.▪사전 등록 기간 : 2월 24일 (월) ~ 3월 27일 (목)▪신청 방법 : 홈페이지 내 '참가 신청하기'를 통해 사전 등록▪홈페이지 : https://pleos.ai/pleos25▪참가비 : 무료👉문의Pleos 25 운영 사무국 pleos.help@hyundai.com

모빌리티개발자컨퍼런스강연행사자동차모빌리티IT정보

채널톡 아이콘