블로그

LC-02s

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

학습 내용 요약 인프런 워밍업 클럽 3기 풀스택 스터디 2주차에는 Supabase Storage를 활용하는 방법을 다루었습니다. 인프런에서 발자국을 작성할 때 강의를 보지 않고도 강의 내용을 파악할 수 있을 만큼 자세한 내용을 작성하는 건 지양해 달라고 가이드한 만큼 강의에 대한 필기는 최소화 하고 회고 위주로 적어보겠습니다. Supabase Strorage 기능 간단 정리Supabase의 Storage 기능은 파일 저장을 위한 서비스로, AWS S3와 같은 오브젝트 스토리지 기능을 제공함. Next.js, React, Flutter, Node.js 등 다양한 환경에서 사용할 수 있으며, PostgreSQL 기반의 권한 관리(RLS)를 지원하는 것이 특징임. 1. 파일 저장 및 관리이미지, 동영상, 문서 등 다양한 파일 형식을 저장 가능파일 업로드, 다운로드, 삭제, 이동 등의 기능 제공버킷(Bucket) 단위로 파일을 관리2. 권한 및 보안 (RLS)Row Level Security (RLS): PostgreSQL과 동일한 방식으로 접근 권한을 설정 가능공개(Public) 및 비공개(Private) 버킷 지원JWT 기반 인증을 사용하여 사용자별 접근 제한 가능3. 파일 접근 방식퍼블릭 파일: 누구나 URL을 통해 접근 가능프라이빗 파일: 인증된 사용자만 접근 가능 (Signed URL 활용)서명된 URL (Signed URLs): 일정 시간 동안만 유효한 URL 생성 가능4. 폴더 및 파일 구조디렉토리(폴더) 개념 지원폴더 내에서 파일 정리 및 관리 가능5. API 및 SDK 지원Supabase Client SDK를 통해 간편한 파일 업로드 및 관리 가능RESTful API 제공 (HTTP Client를 사용하여 직접 호출 가능)6. 이미지 변환 및 최적화Supabase Storage Image Transformation 지원 (이미지 크기 조절, 포맷 변경 등)CDN을 통해 빠른 이미지 제공 가능  Dropbox 클론 미션 회고 풀스택 스터디 2주차 미션은 강의에서 진행하는 Next.js와 Supabase Storage를 활용한 Dropbox 클론 앱에 사진 별 마지막 수정 시간을 표시하는 것이었지만, 저는 기타 편의기능을 더 추가해 보았습니다. 진행했던 미션은 해당 링크에서 보실 수 있습니다.  미션 수행 내용아래는 제가 수행한 미션에 대한 내용을 정리해보았습니다. 기능 명세이미지 파일 업로드 기능드래그 앤 드롭 기능다중 업로드 기능업로드한 이미지 파일 조회 기능키워드 검색 기능이미지 파일 다운로드 기능업로드한 이미지 파일 수정 기능이미지 파일명 변경 기능업로드한 이미지 파일 삭제 기능 강의에서는 기본적으로 파일 입출력에 관련된 기능만 다루었지만, 저는 실제 사용성을 고려하여 파일 업로드 전 미리보기 기능, 이미지 다운로드 기능, 파일 이름 변경 기능 등을 추가해 보았습니다. 또한 강의에서는 react-dropzone 라이브러리를 사용해서 드래그 앤 드롭 기능을 구현하였지만, 저는 프로젝트의 기본적인 스타일 프레임워크로 만타인을 사용하고 있었기 때문에, 만타인에서 따로 지원하는 @mantine/dropzone 라이브러리를 사용하여 구현하였습니다. 미션에 사용한 기술들은 아래에 따로 정리해 두었습니다. 사용할 때마다 느끼는 거지만 만타인은 정말 편한 것 같아요. 사용 기술프레임워크: Next.js v15, React v19데이터베이스: Supabase서버 상태관리: Tanstack Query v5클라이언트 상태관리: Zustand v5스타일 프레임워크: TailWindCSS v3, Mantine v7, Tabler Icons모노레포: Turbo Repo패키지 매니저: pnpm   트러블 슈팅아래는 미션을 진행하면서 만났던 문제들을 해결하는 과정에 대한 내용입니다. 파일명에 한글이 포함될 경우 Supabase Storage에 업로드하지 못하는 문제이미지 파일 이름에 한글이 포함될 경우 업로드가 되지 않는 문제가 있었습니다. 이슈를 찾아보니 Supabase Storage의 정책적인 문제였고, AWS의 S3 서비스도 동일한 문제를 가지고 있었기에 아래 조치들을 취하였습니다. 조치 1.처음 취했던 조치는 아래와 같이 nanoid 라이브러리를 활용하여 중복되지 않는 이름을 생성 후 기존의 파일 이름을 대체하는 방식을 사용했었습니다. 하지만 해당 방식을 사용하면 기존의 파일 이름이 사용자가 식별하지 못하는 값으로 대체되는 문제와, 중복되는 파일을 확인할 수 있는 방법이 없어지는 문제가 있어 최종적으로는 사용하지 않았습니다.'use server' import { nanoId } from 'nanoid' export const uploadImages = async ({ files, }: UploadImagesParams): Promise<{ data: { id: string; path: string } | null }[]> => { const client = await createServerSupabaseClient() return await Promise.all( files.map((file) => { const extension = extractExtension(file.name) const path = `/${nanoId() + '.' + extension}` return client.storage .from(process.env.SUPABASE_BUCKET_NAME!) .upload(path, file, { upsert: true }) }), ) } 조치 2.두 번째로 취한 조치는 조금 번거롭긴 하지만 파일과 1 대 1 로 대응되는 데이터베이스 테이블을 만들어서 관리하는 방식을 사용하였습니다. Supabase에서 지원하는 uuid를 활용하여 테이블의 Primary Key를 설정해 주었고, 이미지 업로드 시 먼저 테이블에 기존 파일 이름을 기반한 데이터 insert 후 생성된 uuid를 사용하여 파일명을 재설정하는 방식으로 우회하였습니다. Supabase에서 지원하는 uuid를 사용했기에 nanoid 같은 별도의 식별자 생성 라이브러리를 관리하지 않을 수 있었습니다.export const uploadImages = async ({ files, }: UploadImagesParams): Promise<{ data: { id: string; path: string } | null }[]> => { const client = await createServerSupabaseClient() const databaseQueries = files.flatMap((file) => { return client .from('minibox') .upsert({ name: file.name }) .select() .then((result) => result.data?.[0] ?? null) }) const targetFiles = await Promise.all(databaseQueries) const storageQueries = targetFiles.map((data) => { if (!data) { return { data: null } } const extension = extractExtension(data.name) const path = `/${data.id + '.' + extension}` const file = files.find((file) => file.name === data.name)! return client.storage .from(process.env.SUPABASE_BUCKET_NAME!) .upload(path, file, { upsert: false }) }) return await Promise.all(storageQueries) }업로드한 파일명이 한글일 경우 올바르게 검색 되지 않는 문제 (feat. MacOS)MacOS 환경에서 업로드한 파일을 별도의 후처리 없이 그대로 데이터베이스에 업로드 했더니 한글이 포함된 파일명에 대해서 아래와 같이 문자열 포함 여부를 판단하는 ilike 쿼리가 제대로 동작하지 않는 문제가 있었습니다.export const getImages = async ({ query = '' }: GetImagesParams): Promise<DroppedImageFile[]> => { const client = await createServerSupabaseClient() const imagesDataAll = await client.from('minibox').select('*').ilike('name', `%${query}}%`) } 원인을 분석해보니 아래와 같이 파일 이름에 한글이 포함되어 있을 시 자음과 모음이 모두 분리된 상태로 저장되어 있어 특정 키워드 포함 여부를 올바르게 판단하지 못해 발생한 문제였습니다.// input 'temp-훈이머리귤.jpeg'.split('')조치기존에 사용하던 ilike 쿼리를 제거하고, 자바스크립트에서 지원하는 String.prototype.normalize 메서드를 사용하여 기존 데이터에 대한 정규형 정준 결합(Normalization Form Canonical Composition) 절차를 거친 후 필터링을 거치는 방법으로 해결하였습니다.export const getImages = async ({ query = '' }: GetImagesParams): Promise<DroppedImageFile[]> => { const client = await createServerSupabaseClient() const imagesDataAll = await client.from('minibox').select('*') const targetData = imagesDataAll.data .filter(({ name }) => name.normalize('NFC').includes(query)) .map(({ id, name }) => `${id + '.' + extractExtension(name)}`) }  후기저는 이제껏 프론트엔드 개발을 접해보면서 한 번도 이미지 파일에 관련된 기능을 작업해보지 않았었습니다. 물론 서버 액션을 사용해서 조금 더 간략한 인터페이스를 사용했기에 실제 FormData 인터페이스를 사용하여 통신 로직을 작성하는 경험은 해보지 못했다는 한계가 있지만, 이번 미션을 통해 자바스크립트로 이미지 파일을 핸들링하는 방법과, Storage 서비스를 연동하여 저장까지 모두 접해볼 수 있어서 개인적으로는 만족스러웠던 한 주였던 것 같습니다. 긴글 읽어주셔서 감사합니다. ☺  

프론트엔드워밍업클럽3기풀스택Next.jsSupabase

정예은

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

박소윤

[워밍업 클럽 3기 Backend 클린코드 & 테스트코드] 세 발자국 👣

이 글은 박우빈 강사님의 Readable Code: 읽기 좋은 코드를 작성하는 사고법 강의를 기반으로 인프런에서 진행하는 [워밍업 클럽 3기] Backend 클린 코드 & 테스트 코드 스터디 회고록입니다.아래 학습 내용은 박우빈 강사님의 Readable Code: 읽기 좋은 코드를 작성하는 사고법 강의를 듣고 학습한 내용입니다. ✏3주차 학습 내용3주차에 학습한 내용은 크게 세 부분으로 나눌 수 있을 것 같다.이전에 Spring 프로젝트들을 수행하면서 테스트 코드를 작성할 때는 Business Layer에 대한 테스트만 진행해왔었다. Controller 단을 테스트해보려고 해도 외부에서 들어오는 요청이나 이런 부분들에 대해서 어떻게 처리를 해줘야하는지 잘 모르겠었고 프로젝트 마감기한으로 인해 그 부분에 대해 자세히 공부를 해 볼 시간이 부족했기 때문이다. 또 실질적으로 로직이 수행되는 Business 단만 잘 돌아가면 문제가 발생할 일이 거의 없지 않을까 하는 생각이었다. 그런데 이번 기회에 강의를 들으면서 그 부분들을 코드를 통해 실습하면서 학습할 수 있어 좋았다.1⃣ Pesistence Layer 테스트Pesistence Layer은 Data Access 역할이므로 비즈니스 가공 로직이 포함되면 안되며 오직 Data에 대한 CRUD에만 집중해야만 한다.2⃣ Business Layer 테스트Business Layer의 역할은 이름처럼 비즈니스 로직을 구현하는 역할로 Persistence Layer와 상호작용(Data를 읽고 쓰는 행위)을 하여 비즈니스 로직을 전개시키며 트랜잭션을 보장해야 한다.3⃣ Presentation Layer 테스트Presentation Layer은 외부세계의 요청을 가장 먼저 받는 계층으로 파라미터에 대한 최소한의 검증을 수행해야 한다. 하위 Layer들을 Mocking 처리를 해 테스트를 수행할 수 있다. 📜미션이번 주는 Day 11에 미션을 수행했다. 미션에 관련한 내용은 아래 링크에서 확인할 수 있다.Mission 4 | Day 11MISSION 4https://github.com/ParkSoyun/inflearn-pwb-readable-code/pull/2 👍Keep: 잘한 점지난주 회고에서 이번 주 미션은 꼭 코드 리뷰를 신청하겠다고 다짐했었는데, 그 약속을 지켰다!사실 제출한 코드가 최선이라고 확신할 수 없었고, 너무 별 내용 없는 코드라서 리뷰 신청을 망설였다. 하지만 결국 마감 1분 전에 용기 내어 신청 완료! 신청자가 많아서 내 코드가 실제로 리뷰를 받을 수 있을지는 모르겠지만, 다음 주 중간 점검 시간에 다른 사람들의 코드와 비교되어 기가 죽지 않을까 살짝 걱정되기도 한다.👎Problem: 개선이 필요한 점마지막이 다가오면서 조금은 느슨해지는 느낌이 든다. 특히 이번 강의는 이전 강의들보다 한 강의의 길이가 길어서, 그 긴 흐름을 따라가는 게 쉽지 않았다. 하지만 결국 해냈다!🤙Try: 더 나은 결과를 위해 시도할 점이제 딱 일주일 남았다. 끝까지 포기하지 않고, 마지막까지 최선을 다해 마무리할 것이다! 💪

개발 · 프로그래밍 기타워밍업클럽워밍업클럽3기

ykm8864

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

4주차 발자국입니다. 공통 예외 처리 및 응답 구조 설계AdminException과 @RestControllerAdvice를 활용한 공통 예외 처리 구현ApiResponse<T> 클래스를 통한 일관된 응답 메시지 제공  Form 및 Table DTO 설계FormElementDTO를 상속받은 다양한 Form 요소 구현 (Text, Date, Select)TableDTO로 서버에서 테이블 구조와 데이터를 내려주는 방식 Interceptor 및 동적 메뉴 구성AdminInterceptor를 활용하여 메뉴 동적 렌더링 구현대메뉴(MenuDTO)와 소메뉴(PageDTO) 구조 설계 및 사이드바 연동 조회 페이지 개발 (연관관계 유무 구분)연관관계 없는 테이블 (e.g., Link, Skill)연관관계 있는 테이블 (e.g., Project-Detail, Experience-Detail)복잡한 구조에 맞춘 TableDTO.from() 사용법 숙지 삽입 및 수정 API 개발@Validated, @NotBlank, @Positive 등을 활용한 Form 유효성 검증연관관계에 따라 다양한 save/update 로직 분기 처리JPA의 더티 체킹을 이해하고, 적절히 save() 호출 생략 뷰 개발 및 템플릿 구조화부트스트랩 템플릿 적용 (BootstrapMade)th:fragment를 활용한 HTML fragment 분리템플릿 관리 편의성 향상Keep(잘한 점)실무에서 vo와 dto의 개념이 혼동되어 쓰이고 있는 경우를 많이 보았는데, 이번 강의를 통해 FormElementDTO와 TableDTO를 도입해, 프론트-백 간 협업에서 구조를 명확히 정의하는 것이 중요하다는 것을 이해했다.Interceptor로 메뉴를 동적으로 설정하고, 뷰에서 자동 렌더링되도록 만든 구조는 앞으로 다양한 관리 페이지에서 재사용 가능한 점에 대한 이해했다.@RestControllerAdvice 기반의 예외 처리로 서비스 전반의 일관성을 유지하는 방식을 이해했다.연관관계에 따른 테이블 구성 방식을 정리하며 도메인 간 설계가 훨씬 명확해짐을 이해했다.Problem(아쉬운 점)리플렉션 사용에 대한 성능 이슈에 대하여 명백한 근거를 잘 모르겠던 부분이 있어서 추가 학습이 필요해보인다.연관관계가 복잡한 엔티티일수록 form → entity 매핑이 반복되며 번거로운 거 같은데 지식이 부족한 거 같다. Try(다음에 시도해볼 점)interceptor 외에 AOP를 활용한 공통 관심사 처리를 구현해보려고 한다.예외 응답에 error code 및 field 정보 추가하여 클라이언트 UX 향상시키면 어떨까 라는 생각이 들었다.내가 다니는 회사에서는 admin화면이 존재하지 않아 익숙하지 않은 부분이 있었는데 학습을 진행한 후에는 서비스를 제공함에 있어서 정말 중요한 요소 중 하나라는 생각이 더더욱 들었다. 관리자 화면과 클라이언트 화면은 어떠한 차이를 가져야하는지 더 실무적으로 경험해보고 싶었다.

백엔드백엔드백엔드프로젝트워밍업클럽3기

LC-02s

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

학습 내용 요약인프런 워밍업 클럽 3기 풀스택 스터디 3주차에는 Supabase Database를 활용하여 페이지네이션을 다루는 방법을 학습하였습니다. 인프런에서 발자국을 작성할 때 강의를 보지 않고도 강의 내용을 파악할 수 있을 만큼 자세한 내용을 작성하는 건 지양해 달라고 가이드한 만큼 강의에 대한 필기는 최소화 하고 회고 위주로 적어보겠습니다. Supabase Client Query Builderlet { data: movie, error } = await supabase .from('movie') // 'movie' 테이블에서 데이터를 가져옴 .select("*") // 모든 컬럼을 선택 // 필터 조건 (WHERE 절과 유사) .eq('column', 'Equal to') // column이 'Equal to' 값과 같은 경우 .gt('column', 'Greater than') // column이 지정 값보다 큰 경우 (>) .lt('column', 'Less than') // column이 지정 값보다 작은 경우 (<) .gte('column', 'Greater than or equal to') // column이 지정 값보다 크거나 같은 경우 (≥) .lte('column', 'Less than or equal to') // column이 지정 값보다 작거나 같은 경우 (≤) .like('column', '%CaseSensitive%') // column이 특정 패턴과 일치하는 경우 (대소문자 구분, LIKE '%...%') .ilike('column', '%CaseInsensitive%') // column이 특정 패턴과 일치하는 경우 (대소문자 구분 없음, ILIKE '%...%') .is('column', null) // column 값이 NULL인 경우 .in('column', ['Array', 'Values']) // column이 지정된 배열 값들 중 하나와 일치하는 경우 (IN 연산자) .neq('column', 'Not equal to') // column이 지정된 값과 다른 경우 (!=) // 배열 관련 필터 .contains('array_column', ['array', 'contains']) // array_column이 주어진 배열 요소를 모두 포함하는 경우 .containedBy('array_column', ['contained', 'by']) // array_column이 지정된 배열에 완전히 포함되는 경우 // 논리 연산자 .not('column', 'like', 'Negate filter') // column이 'like' 조건을 만족하지 않는 경우 (NOT) .or('some_column.eq.Some value, other_column.eq.Other value') // OR 연산자: some_column이 'Some value'이거나 other_column이 'Other value'인 경우 Supabase에서 Text와 Varchar 이해하기요약두 유형 모두 문자열을 저장하는 목적으로 사용됨저장 방식과 성능에 대한 차이가 있지만, Supabase는 단순성을 강조하므로, 특별한 이유가 없다면 기본적으로 text를 사용하는 것이 권장됨 Text긴 문자열을 저장하는 데 사용됨문자 개수에 대한 제한이 없으며, 길이를 예측하기 어려운 문자열에 적합함VarcharVariable Character Length(가변 길이 문자)의 약어최대 길이를 설정할 수 있음 → 데이터 일관성을 유지하는 데 유용하며, 특정 쿼리에서 성능이 약간 향상될 수 있음  페이지네이션 구현 방식: Offset vs CursorOffset Based Pagination 동작 방식OFFSET과 LIMIT을 사용해 특정 범위의 데이터를 가져옴장점특정 페이지로 바로 이동 가능 (예: 1페이지, 5페이지 등)직관적이고 구현이 간단함단점데이터가 많아질수록 OFFSET 성능 저하 (큰 OFFSET 값일수록 느려짐)데이터가 변경되면 순서가 달라질 수 있어 불안정함Cursor Based Pagination동작 방식마지막 항목의 특정 필드(예: created_at 또는 id)를 커서로 사용해 이후 데이터를 가져옴 장점성능이 우수함 (특히 큰 데이터셋에서 OFFSET 사용 없이 빠르게 조회 가능).데이터가 변경되더라도 안정적인 페이지네이션이 가능함.단점특정 페이지로 바로 이동이 어렵고, 이전 페이지로 돌아가는 것이 복잡할 수 있음.구현이 상대적으로 복잡함.  Netflix 클론 미션 회고풀스택 스터디 3주차 미션은 강의에서 진행하는 Next.js와 Supabase Database를 활용한 Netflix 클론 앱에 찜 기능을 추가하는 것이었습니다. 진행했던 미션은 해당 링크에서 보실 수 있습니다. 미션 수행 내용아래는 제가 수행한 미션에 대한 내용을 정리해보았습니다. 영화 목록 조회 기능찜 리스트 조회 기능키워드 검색 기능무한 스크롤 지원영화 상세 정보 조회 기능동적 메타데이터 지원SSR 지원영화 찜하기 기능낙관적 업데이트 지원  사용 기술프레임워크: Next.js v15, React v19데이터베이스: Supabase서버 상태관리: Tanstack Query v5클라이언트 상태관리: Zustand v5스타일 프레임워크: TailWindCSS v3, Mantine v7, Tabler Icons모노레포: Turbo Repo패키지 매니저: pnpm 페이지네이션강의에서는 옵셋 기반으로 페이지네이션을 구현하였지만, 저는 커서 기반 페이지네이션이 데이터 추가 또는 삭제 시 페이지 별 인덱스가 꼬여 다른 페이지에 같은 데이터가 존재하거나 데이터를 건너뛸 수 있는 문제가 없기에 무한스크롤 기능에 조금 더 적합하다고 판단하여 커서 기반으로 페이지네이션을 구현하였습니다.export interface SearchMoviesParams { keyword?: string cursor?: number | null size?: number like?: boolean } export interface SearchMovies { (params: SearchMoviesParams): Promise<{ data: Movie[] nextCursor: number | null first: boolean last: boolean }> } export const searchMovies: SearchMovies = async ({ cursor = null, keyword = '', like = false, size = 12, }: SearchMoviesParams) => { const client = await createServerSupabaseClient() const first = !cursor let query = client.from('movie').select('*').order('id', { ascending: true }) if (keyword) { query = query.ilike('title', `%${keyword}%`) } if (like === true) { query = query.eq('is_like', true) } if (cursor) { query = query.gt('id', cursor) } const { data, error } = await query.limit(size + 1) if (error) { console.log(error) return { data: [], nextCursor: null, first, last: true, error } } const hasNextPage = data.length > size const nextCursor = hasNextPage ? (data[size - 1]?.id ?? null) : null return { data: ( data?.map((movie) => ({ id: movie.id, title: movie.title, imageURL: movie.image_url, overview: movie.overview, popularity: movie.popularity, releaseDate: movie.release_date, voteAverage: movie.vote_average, isLike: movie.is_like, })) ?? [] ).slice(0, size), nextCursor, first, last: !hasNextPage, } }  찜 기능 테이블 스키마아래는 3주차 미션인 찜 기능을 구현할 때 작성한 movie 테이블 스키마입니다.CREATE TABLE movie ( id SERIAL PRIMARY KEY, image_url TEXT NOT NULL, title TEXT NOT NULL, overview TEXT NOT NULL, vote_average FLOAT8 NOT NULL, popularity FLOAT8 NOT NULL, release_date DATE NOT NULL, is_like BOOLEAN NOT NULL DEFAULT FALSE );해당 프로젝트에서는 별도로 회원 관리를 하지 않기 때문에 간단하게 컬럼을 하나 추가하여 구현하였지만, 만약 회원이 존재하는 상황이라면 테이블을 따로 분리한 후 회원 id와 영화 id를 받아와서 왜래키(FK)로 관리하는 방식도 괜찮았을 것 같습니다.  후기이번 주차에는 백엔드의 대표적인 작업 중 하나인 페이지네이션을 학습해 볼 수 있었습니다. 강의와는 다르게 Supabase를 사용한 커서 기반 페이지네이션을 구현해 보았는데, 공식 문서를 읽는 것이 쉽지 않았던 것 같습니다. 이전 주차들에서도 동일하게 Supabase 클라이언트에서 제공하는 쿼리 빌더 메서드들이 무슨 역할을 하는지 정리 해야겠다고 생각했는데 이번 주차에 드디어 하게 되었네요. 이제 다음 주차 미션인 인스타그램 클론까지 학습하면 간단한 MVP는 혼자서 구현해 볼 수 있을 것 같아서 기대됩니다. 긴글 읽어주셔서 감사합니다. ☺    

프론트엔드워밍업클럽3기풀스택Next.jsSupabase

codestudy

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

[인프런 워밍업 스터디 클럽 3기 PM/PO] 3주차 발자국📚 주간 학습 내용 요약1. 지표(Metric)의 기본 개념과 중요성지표는 사업과 제품의 현황/성과를 정량화한 측정 도구상시 모니터링 지표(DAU, MAU, 매출 등)와 특정 상황 확인 지표로 구분완벽한 지표보다는 팀의 집중력을 높이는 실용적 지표가 중요2. Acquisition(고객 획득) 지표CAC(Customer Acquisition Cost): 고객 1명 획득 비용LTV(Customer Lifetime Value): 고객 1명이 가져다주는 총 이익Payback Period: CAC 회수 기간채널별 CAC 측정이 중요하며, 현금 흐름 관리가 핵심3. Activation(활성화) 프레임워크Setup Moment → Aha Moment → Habit Moment의 3단계 구조신규 사용자가 제품의 가치를 경험하고 습관화하는 과정각 단계별 전환율을 측정하고 개선하는 것이 중요4. Engagement(참여도) 측정법Breadth(얼마나 많은 사용자): DAU, WAU, MAUDepth(얼마나 깊게 사용): 기능 사용 깊이, 사용 시간Frequency(얼마나 자주 사용): DAU/MAU, Lness 지표Efficiency(과업 성공률): 매칭률, 완료율 등5. Retention(유지율)의 중요성"Retention is King" - 성장의 핵심 요소리텐션 측정 방식: 코호트 리텐션, Day N 리텐션, Bracket/Unbounded 리텐션산업별로 다른 리텐션 기준 존재 (소셜, 트랜잭션, SaaS 등)6. Event-Based Analytics 기본 개념이벤트: 사용자와 제품 간 상호작용 (페이지 조회, 버튼 클릭 등)이벤트 프로퍼티: 이벤트의 상세 정보 (카테고리, 이름, ID 등)데이터 트래킹: Client-Side와 Server-Side 방식의 장단점7. Event Taxonomy 설계 방법Top-Down 접근: 목적에서 시작해 필요 데이터 정의Bottom-Up 접근: 제품 핵심 이벤트에서 시작명확한 이벤트 명명 규칙과 속성 정의가 중요🌟 핵심 학습 인사이트배운 점지표 설정은 과학보다는 '예술'에 가까우며, 팀의 집중을 이끌어내는 스토리텔링이 중요함완벽한 지표보다는 비즈니스 성과에 도움되는 실용적 지표가 더 가치 있음사용자 여정에 따라 단계별(Acquisition → Activation → Engagement → Retention → Monetization) 지표 설정 필요데이터 환경 구축은 투자가 필요한 중요 자산이며 체계적 설계가 필수적💭 미션에 대한 회고코촉촉 서비스의 프로덕트 지표를 설정하면서, 일반적인 프레임워크를 넘어 서비스만의 고유한 가치(실시간 위치 공유, 맞춤형 매칭, 긴급 요청 대응 등)를 측정할 수 있는 지표 개발의 중요성을 깨달았습니다. 지표가 단순한 수치가 아닌 구체적인 '의사결정 도구'로서 역할을 한다는 점과, 타겟 사용자(바쁜 현대인, 어린 강아지)의 특성을 반영한 지표 설계가 서비스의 방향성과 가치를 명확히 한다는 것을 배웠습니다. 이번 미션을 통해 프로덕트 지표 설정이 기술적 작업이 아닌 제품의 핵심 가치와 비전을 명확히 하는 전략적 과정임을 이해하게 되었습니다.

기획 · PM· PO워밍업클럽3기pmPM

워밍업 클럽 3기_PM/PO_2주차 발자국

강의 정보강의명: 시작하는 PM/PO들에게 알려주고 싶은, 프로덕트의 모든 것강사명: 김민우링크: https://inf.run/gQh4a강의 내용고객과 직접 만나야 하는 이유고객을 직접 만나는 것이 고객에 대한 이해를 높이는 가장 좋은 방법이다. 고객과 서비스에 대해 직접 문답하면서 고객이 가진 멘탈 모델을 정확하고, 구체적으로, 해상도 높게 가져갈 수 있다.중간 매개자로부터 받는 고객의 의견은 매개자의 시선에서 생략 및 요약되기 때문에 핵심 문제들을 놓치게 될 수 있다. 고객 리서치 방법의사결정을 명확하게 하고, 그에 맞는 리서치 방법과 계획을 세워야 한다.1:1 심층 인터뷰제품 사용 동기, 경험, 문제점 등에 대한 고객의 의견을 수집하는 방식이다.행동이 아니라 말에 의존해서 정보를 수집한다는 점을 유의해야 한다.인터뷰 대상은 10명을 기본으로 추천하나, 의사결정을 하기 충분한 답변을 모았다면 멈춰도 된다.사업>리서치>인터뷰로 질문을 만들고 준비해야 한다. 사업 상의 목적을 명확히 하고, 이를 알아가기 위해 필요한 리서치 방향을 생각한 뒤에 인터뷰 대상자가 답변 가능한 쉬운 질문으로 다듬어 가야 한다. 인터뷰 가이드 작성이 필요하다.답변은 유형화, 전반적인 테마를 발굴하여 중요 내용을 추려서 활용한다.사용성 테스트유저가 사용, 이해, 기능을 발견할 수 있는 제품이 좋은 제품이다.매몰 비용을 최소화하기 위해 사용성 테스트를 진행해야 한다.어림 법칙에 따라 5명 정도면 테스트는 충분하다. 그러나 유저 그룹에 여러 개라면 이를 고려하여 진행해야 한다.테스트 진행 시에는 2인 1조, 녹화 및 녹음이 필수이다.PM이 갖춰야 할 데이터 역량데이터 축적 역량과 활용역량이 필요하다.축적 역량은 로그 데이터를 설계하는 것을 의미한다.활용 역량은 지표를 이해하고 해석하는 것이다.데이터를 쪼개서 자세히 바라보는 연습이 필요하다.후기사용자의 의견을 듣기 위해 온라인 설문조사를 진행한 경험이 있었다. 그땐 가이드가 없어서 알고자 하는 목적에 집중해서 문항을 작성했는데, 지금 생각해 보니 너무 직접적인 질문이었던 것 같다. 목적을 선명히 해야 이후의 작업들을 펼치고 모을 수 있다는 것을 배웠다.국내가 아닌 국외 사용자들은 어떻게 심층 인터뷰나 사용성 테스트를 진행하는 게 좋을지 고민해 보게 되었다.데이터를 활용한 업무를 진행하기 위해서는 그런 환경이 놓인 회사에 가는 것이 필요하다는 이야기가 맘에 와닿았다. 데이터를 수집하기 위해서는 앞서 선행되어야 할 작업들이 많고, 후에 해석에도 많은 시간이 걸린다. 이를 낭비라고 생각하지 않는 회사로 가려면 면접에서 어떤 것을 확인해 봐야 하는 지 궁금해졌다. 미션앱 고도화 작업 이후에 특정 기능을 활용하는 사용자의 수가 감소한 사례가 있었다. 경험한 사례를 기준으로 고객 조사 방법을 설계해보려 한다.  

기획 · PM· PO워밍업클럽3기

박소윤

[워밍업 클럽 3기 Backend 클린코드 & 테스트코드] 두 발자국 👣

이 글은 박우빈 강사님의 Readable Code: 읽기 좋은 코드를 작성하는 사고법 강의를 기반으로 인프런에서 진행하는 [워밍업 클럽 3기] Backend 클린 코드 & 테스트 코드 스터디 회고록입니다.아래 학습 내용은 박우빈 강사님의 Readable Code: 읽기 좋은 코드를 작성하는 사고법 강의를 듣고 학습한 내용입니다.저작권 보호를 위해 강의 내용보다는 학습하면서 느낀 점 위주로 정리하였습니다. 2주차는 아쉬움이 많이 남는 한 주였다. ✏2주차 학습 내용2주차에 학습한 내용은 크게 네 가지로 나눌 수 있을 것 같다.1⃣ 코드 다듬기Section 6에서는 코드 다듬기에 대해 학습하였다.코드 다듬기와 관련된 내용은 항상 궁금했지만, 너무 사소한 것 같아 어디에 물어보기가 어려웠다. 그런데 이번 강의에서 그런 부분들이 다뤄져서 무척 유익했다.특히, 변수와 메서드 나열 순서에 대한 강의가 가장 기억에 남는다. 변수의 경우, 나는 항상 사용하는 순서대로 나열해왔기 때문에 비교적 괜찮았지만, 메서드 나열 순서는 평소 내가 작성하던 방식과 차이가 있었다.강의에서는 두 가지 예시를 들었는데, 나는 그중 첫 번째 예시(public-private-private / public-private)와 비슷한 방식으로 메서드를 정리해왔다. 그렇게 하는 것이 가독성에 더 좋다고 판단했기 때문이다. 하지만 강의를 듣고 나니, 내가 작성한 방식은 코드를 작성한 사람에게는 가독성이 좋을 수 있지만, 처음 보는 사람에게는 그렇지 않을 수도 있겠구나 하는 생각이 들었다.아직 대규모의 코드를 작성해 본 경험이 부족해서, 이런 차이를 체감하지 못했던 것일 수도 있다. 앞으로도 이 부분에 대해 더 고민해보고, 실제 프로젝트에서 적용해 보며 경험을 쌓아야겠다고 생각했다.2⃣ 리팩토링 실습화요일에는 미션 7을 통해 실제 코드를 리팩토링하는 경험을 해보았다.강의를 보면서 따라할 때는 강사님의 사고를 그대로 따라갈 수 있어 어려운 점이 없었지만, 막상 혼자서 리팩토링을 해보려니 예상보다 훨씬 어려웠다.가장 먼저 부딪힌 문제는 다른 사람이 작성한 코드를 읽고 이해하는 데 걸리는 시간이었다. 지금까지는 내가 작성한 코드만 읽고 리팩토링해왔기 때문에, 처음 보는 코드의 구조를 파악하는 일이 생각보다 쉽지 않았다.또한, 가장 최선의 답을 찾아야 한다는 점도 어려웠다. 리팩토링에는 정답이 존재하는 것이 아니라, 주어진 상황에서 최적의 해결책을 찾는 과정이기 때문에 더 고민이 많아졌다.객체의 책임을 적절히 나누는 것도 쉽지 않았다. 객체의 책임이 명확하게 하나로 분리된 것인지를 판단하는 것부터가 어렵게 느껴졌다. 게다가, SOLID 원칙에 맞게 코드가 잘 설계되었는지 파악하는 과정조차도 쉽지 않았다.강의를 들으며 실습할 때는 리팩토링이 생각보다 어렵지 않다고 착각했지만, 실제로 혼자 진행해보니 엄청난 연습이 필요하다는 걸 절실히 깨달았다.무엇보다도, 리팩토링을 잘하기 위해서는 먼저 다른 사람의 코드를 읽는 능력부터 길러야겠다고 다짐한 시간이었다.3⃣ 중간 점검금요일에는 약 2시간 동안 중간 점검 라이브를 진행했다.코드 리뷰를 신청해보고 싶었지만, 아직 다른 사람들 앞에서 내 코드를 공개할 자신이 없어 망설이다가 기회를 놓치고 말았다. 그 점이 많이 아쉬웠지만, 대신 다른 사람들의 코드 리뷰를 보며 설명을 듣는 것만으로도 큰 도움이 되었다.이번 라이브는 좋은 배움의 기회가 되었고, 다음에는 꼭 용기를 내어 코드 리뷰를 직접 신청해봐야겠다고 다짐했다.4⃣ 자바 기반의 테스트 코드학부생 때부터 연구실에서 프로젝트를 진행할 때 테스트 코드를 작성하고, 요구사항에 대한 테스트를 항상 진행해왔다. 덕분에 테스트 코드의 필요성에 대해서는 늘 인식하고 있었지만, Junit과 같은 자동화된 도구를 사용하지 않고 수동 테스트만 진행해왔다.그렇다 보니 테스트 도구를 배워야겠다는 필요성을 계속 느끼면서도, 제대로 공부할 기회가 없었다. 하지만 이번 스터디를 통해 마침내 테스트 도구를 배울 수 있어서 정말 좋은 경험이 되었다. 📜미션이번 주는 Day 7에 미션을 수행했다. 미션에 관련한 내용은 아래 링크에서 확인할 수 있다.Mission 3 | Day 7MISSION 3https://github.com/ParkSoyun/inflearn-pwb-readable-code/pull/1 👍Keep: 잘한 점이번 주는 전체적으로 아쉬움이 많이 남는 한 주였다.그럼에도 불구하고, 좋지 않은 컨디션 속에서도 해야 할 일들을 포기하지 않고 끝까지 수행했다는 점은 스스로 칭찬해주고 싶다.👎Problem: 개선이 필요한 점컨디션이 좋지 않은 상태에서 무리하다 보니 한 주 내내 몸이 아팠고, 결국 완주를 위해 꼭 필요한 최소한의 일만 수행할 수밖에 없었다는 점이 아쉽다.다행히 주말 동안 충분히 휴식을 취하며 컨디션을 회복했으니, 다음 주부터는 다시 집중해서 달려야겠다.🤙Try: 더 나은 결과를 위해 시도할 점이번 주를 통해 컨디션 관리가 가장 중요하다는 것을 절실히 깨달았다. 앞으로는 몸 상태를 잘 살피면서 최선을 다해야겠다는 다짐을 했다.또한, 월요일 미션을 빠르게 마친 후 코드 리뷰를 신청하는 것을 목표로 삼았다. 사실 아직 다른 사람들 앞에서 내 코드를 공개하는 것이 부끄럽지만, 이번 중간 점검 때 다른 사람의 코드를 보는 것만으로도 큰 도움이 되었다. 그렇다면 내 코드로 직접 리뷰를 받으면 더욱 많은 인사이트를 얻을 수 있지 않을까?조금 더 용기를 내서 이번 미션에서는 코드 리뷰를 꼭 신청해봐야겠다.

개발 · 프로그래밍 기타워밍업클럽워밍업클럽3기

ykm8864

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

학습 내용2주차 발자국입니다.1. 데이터베이스 및 리포지토리데이터베이스 초기화: H2 및 MySQL을 활용한 데이터베이스 설정과 application.yml 환경 분리리포지토리 개발: JpaRepository<T, ID> 상속을 통해 기본적인 CRUD 메서드 활용리포지토리 테스트 코드 작성: Mock을 활용한 단위 테스트 (@DataJpaTest, @MockBean)리포지토리 성능 개선: N+1 문제 해결2. 엔티티 및 서비스 개발클래스 생성: 엔티티 정의 (@Entity, @Id, @GeneratedValue)DTO 개발: 엔티티와 분리된 data class 정의로 API 응답 최적화서비스 개발: 비즈니스 로직을 @Service 클래스에서 관리하여 Controller와 역할 분리서비스 테스트 코드 작성: @InjectMocks와 @Mock을 활용한 Mockito 기반 테스트3. 컨트롤러 및 API 개발컨트롤러 개발: @RestController와 @Controller를 활용한 API 및 뷰 렌더링 처리컨트롤러 테스트 코드 작성: MockMvc를 활용하여 API 응답 검증 (@SpringBootTest, @AutoConfigureMockMvc)4. Thymeleaf 기반 UI 개발부트스트랩 템플릿 적용: Start Bootstrap을 활용한 기본 레이아웃 구성템플릿 수정index.html: 주요 콘텐츠 및 네비게이션 구성resume.html: 경력 및 기술 스택 표시projects.html: 프로젝트 목록을 동적으로 표시레이아웃: th:fragment를 활용한 네비게이션 및 푸터 분리5. 인터셉터 개발인터셉터 활용: HandlerInterceptor를 구현하여 요청 전/후 로직 추가주요 기능: 인증/인가 체크, 요청 로깅, 공통 헤더 처리Keep (만족했고, 앞으로도 지속하고 싶은 부분)리포지토리와 서비스의 명확한 역할 분리로 유지보수성이 향상된 점을 느꼈다Mock 테스트를 활용하여 서비스 및 컨트롤러 단위 테스트를 효과적으로 구현했는데, 이전에 워밍업클럽에서 테스트코드를 들어서 그런지 쉽게 이해가 됐다Thymeleaf Fragment 적용으로 중복 코드 제거 및 템플릿 유지보수 능력을 길렀다Problem (아쉬웠던 점)jpa 성능 최적화에 대한 지식이 아직 부족한 거 같아서 김영한 강사님의 강의를 수강해볼 예정Thymeleaf에서 동적 데이터 처리의 한계가 있어 React/Vue 같은 프론트엔드 연계를 고민해야 할텐데 관련하여 지식이 너무 적다 Try (다음에 시도해볼 점)N+1 성능문제 개선 및 QueryDSL을 활용한 복잡한 검색 기능 최적화API 개발 시 OpenAPI(Swagger) 문서 자동화 적용Thymeleaf 대신 React와 연동인터셉터 로직이 복잡해질 경우 AOP를 활용하는 방법도 고려할 필요가 있어보이는데 AOP 구현하는 미니프로젝트 해볼 예정

백엔드코틀린백엔드워밍업클럽3기

허진혁

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

코드 리팩토링을 통한 성장: 주간 회고1. 리팩토링을 시작하게 된 계기이번 주는 코드 리팩토링을 집중적으로 진행했다. 기존 코드에서 개선할 점을 찾아 수정하는 과정에서 단순한 문법 개선을 넘어, 코드의 유지보수성과 가독성을 향상시키는 것이 얼마나 중요한지 다시금 깨닫게 되었다.리팩토링을 진행한 코드(PR): GitHub 링크이 과정을 통해 단순한 기능 구현이 아니라, 보다 효율적인 코드 작성법과 객체 지향적인 사고 방식을 익히게 되었다.2. 리팩토링 과정과 개선 포인트(1) 중복 코드 제거 및 메서드 추출기존 코드에서는 비슷한 로직이 여러 메서드에서 반복적으로 사용되고 있었다. 이를 해결하기 위해 메서드 추출 기법을 적용했다.기존 코드에서는 여러 곳에서 동일한 로직을 복사-붙여넣기 했지만, 공통 로직을 별도의 메서드로 분리하여 재사용성을 높였다.이 과정에서 단일 책임 원칙(SRP, Single Responsibility Principle) 을 더욱 깊이 이해할 수 있었다.(2) 가독성을 높이는 네이밍 개선리팩토링 전에는 변수명과 메서드명이 애매하여 코드의 의도를 명확히 파악하기 어려웠다. 개선 과정에서 다음과 같은 기준을 적용했다.메서드명은 동작을 명확하게 설명할 수 있도록 동사 + 목적어 형식으로 변경변수명은 의미를 명확하게 전달할 수 있도록 명명 (예: temp → formattedDate)코드 리뷰 과정에서 네이밍의 중요성을 다시금 깨달았다. 가독성이 높아지면 코드의 이해도가 높아지고, 유지보수도 쉬워진다.(3) 불필요한 의존성 제거 및 클래스 분리기존 코드에서는 한 클래스가 너무 많은 역할을 담당하고 있었다. 이를 해결하기 위해 책임을 분리하고, 역할에 맞는 클래스를 생성했다.기존의 거대한 클래스에서 역할별로 클래스를 분리하여 객체 지향적인 구조로 개선불필요한 의존성을 제거하고, 의존성 역전 원칙(DIP, Dependency Inversion Principle) 을 적용하여 유연성을 높였다.SRP를 적용한 후, 코드의 변경이 필요할 때 한 곳만 수정하면 되어 유지보수성이 크게 향상되었다.3. 다른 개발자들의 경험에서 배운 점이번 리팩토링을 진행하면서 다른 개발자들이 작성한 후기도 참고했다. (Inflearn 블로그 링크 모음)여러 후기에서 공통적으로 강조하는 몇 가지 핵심 사항을 발견했다.리팩토링의 본질은 단순한 코드 변경이 아니라 유지보수성과 확장성을 높이는 것코드가 동작한다고 끝이 아니라, 더 나은 코드로 개선하는 과정이 필요함.리팩토링은 협업과 코드 리뷰를 통해 더욱 효과적으로 이루어진다혼자 작업할 때는 발견하지 못했던 문제점들이, 코드 리뷰를 통해 드러남.다른 개발자들의 시각에서 개선점을 찾는 것이 중요함.객체 지향 원칙을 적용하는 것이 리팩토링의 핵심이다SOLID 원칙을 고려하며 리팩토링할 때 코드가 더욱 구조적으로 개선됨.특히 단일 책임 원칙(SRP), 의존성 역전 원칙(DIP) 을 적용하면 코드의 확장성이 크게 증가함.이번 리팩토링을 통해 나 또한 이 점을 깊이 체감했다.4. 리팩토링을 통해 얻은 교훈이번 경험을 통해 얻은 가장 큰 교훈은 "리팩토링은 단순한 코드 수정이 아니라, 코드의 가치를 높이는 과정이다." 라는 것이다.코드는 팀원과 미래의 나를 위한 문서와 같다. 가독성이 좋고, 유지보수가 쉬운 코드가 진짜 좋은 코드다.코드 리뷰를 적극적으로 활용하자. 다른 개발자들의 피드백을 통해 더 나은 개발자가 될 수 있다.객체 지향 원칙을 익히고 실천하자. SOLID 원칙을 고려하며 개발하는 것이 장기적으로 가장 효율적인 방법이다.이러한 리팩토링 경험을 반복하면서, 더욱 성장하는 개발자가 되어야겠다는 다짐을 하게 되었다. 앞으로도 주간 단위로 배운 내용을 정리하며 지속적인 성장을 기록할 예정이다.✍ 앞으로의 다짐매주 코드 리팩토링을 진행하고, 개선된 내용을 블로그에 정리하기코드 리뷰 문화를 적극적으로 활용하고, 동료 개발자들과 협업하며 성장하기SOLID 원칙과 디자인 패턴을 공부하고, 실무에서 적용할 수 있도록 연습하기이번 리팩토링 경험을 통해 얻은 교훈을 앞으로도 개발 과정에 적용하며, 더 나은 개발자로 성장해 나가겠다!

백엔드워밍업클럽3기클린코드

[워밍업 클럽 3기 - CS 전공 지식] - Day 9 미션 2

운영체제FIFO 스케줄링의 장단점이 뭔가요?장점단순하고 이해하기 쉽다단점실행 시간(burst time)이 짧은 프로세스는 실행 시간이 길더라도 일찍 도착한 프로세스의 완료를 기다려야한다 SJF를 사용하기 여러운 이유가 뭔가요?A. 프로세스의 실행시간을 예측하기 어렵다.  RR 스케줄링에서 타임 슬라이스가 아주 작으면 어떤 문제가 발생할까요?A. 컨텍스트 스위칭을 처리하는 비용이 증가한다. 운영체제가 MLFQ에서 CPU Bound Process와 I/O Bound Process를 어떻게 구분할까요?A. CPU 사용시간에 따라 우선순위 큐에 다르게 배치하면서 구분한다. 예를 들어, CPU 바운드 작업은 타임 슬라이스를 I/O 바운드 작업보다 오래 점유해서 사용하기 때문에, 우선순위가 낮은 큐에 배치시키고, 더 짧게 사용하는 I/O 바운드 프로세스는 우선순위가 더 높은 큐에 배치시킨다. (타임 슬라이스를 사용하는 시간과 요청 빈도에 의한 피드백) 공유자원이란 무엇인가요?A. 프로세스간 통신에서 공통으로 접근해서 이용하게 되는 데이터. 교착상태에 빠질 수 있는 조건은 어떤 것들을 충족해야할까요?상호 배제: 자원은 한번에 하나의 프로세스만 사용비선점: 이미 점유한 자원은 다른 프로세스가 뺏어갈 수 없다점유와 대기: 이미 자원을 점유한 프로세스가 추가적인 자원을 기다리는 상태원형 대기: 프로세스들이 서로가 가지고 있는 자원을 기다리며 대기하는 상태자료구조와 알고리즘재귀함수에서 기저조건을 만들지 않거나 잘못 설정했을 때 어떤 문제가 발생할 수 있나요?A. 함수에서 탈출하지 못하기 때문에 무한하게 재귀를 호출하게 되면서 스택 오버플로우(StackOverflow)가 발생한다.0부터 입력 n까지 홀수의 합을 더하는 재귀 함수를 만들어보세요.A.function sumOdd(n) { if (n <= 0) return 0; // 기저조건 if (n % 2 === 0) return sumOdd(n - 1); return n + sumOdd(n - 2); } console.log(sumOdd(10)) // 25 다음 코드는 매개변수로 주어진 파일 경로(.는 현재 디렉토리)에 있는 하위 모든 파일과 디렉토리를 출력하는 코드입니다. 다음 코드를 재귀 함수를 이용하는 코드로 변경해보세요.import fs from "fs"; import path from "path"; function traverseDirectory1(directory){ const stack = [directory]; // 순회해야 할 디렉토리를 저장할 스택 while (stack.length > 0) { // 스택이 빌 때까지 반복 const currentDir = stack.pop(); // 현재 디렉토리 const files = fs.readdirSync(currentDir); // 인자로 주어진 경로의 디렉토리에 있는 파일or디렉토리들 for (const file of files) { // 현재 디렉토리의 모든 파일or디렉토리 순회 const filePath = path.join(currentDir, file); //directory와 file을 하나의 경로로 합쳐줌 const fileStatus= fs.statSync(filePath); // 파일정보 얻기 if (fileStatus.isDirectory()) { // 해당 파일이 디렉토리라면 console.log('디렉토리:', filePath); stack.push(filePath); } else { // 해당 파일이 파일이라면 console.log('파일:', filePath); } } } } traverseDirectory1("."); // 현재 경로의 모든 하위 경로의 파일, 디렉토리 출력 console.log("------------------------"); function traverseDirectory2(directory){ const files = fs.readdirSync(directory); // 현재 디렉토리의 파일 목록 가져오기 for (const file of files) { const filePath = path.join(directory, file); // 파일 또는 디렉토리의 전체 경로 const fileStatus = fs.statSync(filePath); // 파일정보 얻기 if (fileStatus.isDirectory()) { // 해당 파일이 디렉토리라면 console.log('디렉토리:', filePath); traverseDirectory2(filePath); // 재귀 호출하여 내부 탐색 } else { // 파일인 경우 console.log('파일:', filePath); } } } traverseDirectory2("."); // 현재 경로의 모든 하위 경로의 파일, 디렉토리 출력 

워밍업클럽3기cs미션

영후이

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

스프링 의존성 주입### 1. 생성자```kotlin@ServiceClass PresentaionService(private val presentaionRepository: PresentaionRepository){// 생략}```> 생성자 주입 방식을 권장하는 3가지 이유> 1. 런타임에 수정자를 호출해서 의존성이 바뀌는 것을 방지할 수 있다.> 2. 순환참조 시 컴파일 오류가 발생해서 런타임 단계에서 메소드가 서로 호출하는 스택오버플로우 에러를 방지할 수 있다.> 3. 의존하는 Bean 이 누락되면 컴파일 오류가 발생하기 때문에 런타임에서 NullPointerException 을 방지할 수 있다.### 2. 수정자```kotlin@ServiceClass PresentaionService {private lateinit var presentaionRepository: PresentaionRepository@Autowiredfun setPresentationRepository(presentaionRepository: PresentaionRepository){this.presentaionRepository = presentaionRepository}}```### 필드주입```kotlin@ServiceClass PresentaionService {@Autowiredprivate lateinit val presentaionRepository: PresentaionRepository}```HTTP와 REST API### HTTP 정의> [!tip] HTTP 정의> * Hyper Text Transfer Protocol> * 네트워크로 통신하는 두 컴포넌트 간의 통신 규약### HTTP 요청/응답> Request> * Start Line: HTTP 메서드, URL, HTTP 버전을 표시한다.> * Header: 컨텐츠의 길이나 유형, 클라이언트 (브라우저) 의 정보 등을 표현한다.> * Body: 서버에서 작업을 처리하기 위해 필요한 실질적인 데이터를 담는다. 최근에는 JSON 포맷을 주로 사용한다.> Resposne> * Start Line : HTTP 버전, 상태 코드, 상태 메세지를 표현한다.> * Header: 컨텐츠의 길이나 유형, 서버의 정보 등을 표현한다.> * Body : 응답의 결과 데이터를 담는다. 서버 사이드 렌더링 방식일 경우 HTML 을 사용한다.> 클라이언트 사이드 렌더링 방식이거나 서버 간의 통신일 경우 주로 JSON 을 사용한다.### HTTP 요청 메서드> GET> * READ 작업을 요청할 때 사용한다.> * 브라우저의 주소창은 항상 GET 메서드로 요청한다.> * GET 요청을 할 경우 일부 HTTP 라이브러리에서는 Body 에 데이터를 담을 수 없다.> * 따라서 데이터를 보내야 할 경우 쿼리 파라미터를 주로 사용한다.> POST> * Creat 작업을 요청할 때 사용> * 브라우저에서는 사용할 수 없고, Postman 과 같은 별도의 툴을 사용해야 한다.> PUT> * Update 작업을 요청할 때 사용> PATCH> * Update 작업을 요청할 때 사용> DELETE> * Delete 작업을 요청할 때 사용## 데이터베이스### 데이터베이스 정의> 📌 여러 사람이 공유하여 사용할 목적으로 체계화해 통합, 관리하는 데이터의 집합### DBMS (DataBase Management System)> 📌 데이터의 집합(데이터베이스)을 저장하고 관리할 수 있는 응용 프로그램.### 관계형 데이터베이스> 📌 가장 널리 쓰이는 데이터베이스. 데이터를 행과 열로 이루어진 표의 형태로 저장함.> 각 테이블 들은 관계를 가지기 때문에 관계형 데이터베이스라고 함.#### 대표적인 관계형 데이터베이스> 📒 오라클 ,`Mysql`, PostgreSQl### 비관계형 Database> 📒 mongoDB, ... Redis### JPA> 📌 Java Persistance API 약어, 자바 ORM 기술의 표준 인터페이스### ORM> 📌 Objecte Relational Mapping 약어 , 객체 관계 매핑 -> 객체 지향 프로그래밍의 인스턴스와 관계형 데이터베이스를 매핑해주는 기술을 의미.### 트랜잭션> 📌 여러개의 데이터베이스 작업을 하나로 묶어주는 논리적인 단위.궁금했던 점엔티티 생성 시, 생성자 선언방식을 사용해도 되는가?> 예시 -> @Entity class Achievement( @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "achievement_id") val id: Long? = null, var title: String, var description: String, var achievedDate: LocalDate? = null, var host: String, var isActive: Boolean, ): BaseEntity()위와 같이 엔티티를 생성방식은 중복되는 코드를 줄일 수 있어서 개인적으로 선호하는 방식인데, 이런 방식을 사용해도 되는지 궁금증이 생겼다. [입문자를 위한 Spring Boot with Kotlin](https://www.inflearn.com/course/%EC%9E%85%EB%AC%B8%EC%9E%90-spring-boot-kotlin-%ED%8F%AC%ED%8A%B8%ED%8F%B4%EB%A6%AC%EC%98%A4/dashboard)

워밍업클럽3기

ykm8864

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

학습 내용스프링, jpa, 코틀린을 통해 나만의 포토폴리오 등록 사이트를 개발해보는 첫 발자국입니다.실습 전 이론 학습이 강의의 가장 좋은 점이다. 기초적인 지식을 먼저 학습한 다음 실습을 접할 수 있어 지식의 공백이 생기지 않는다.물론 이미 아는 부분이 많아 가볍게 들었지만 잘하는 개발자 분들을 보면 같은 지식을 본인만의 쉬운 언어로 정리하여 체계적으로 잘 알려주시는 경향이 있다. 이번에도 그러한 점을 느꼈다. 가장 흥미로웠던 부분은 프로젝트 패키지를 나눌 때 였다. 개인적으로 DDD를 학습 한 후로 이상하게 프로젝트 패키지를 나누는 행위가 이전보다 더 부담스럽고 생각이 많아지곤 했다. 하지만 강사님이 프로젝트 패키지를 나누면서 어떤 생각을 하셨고 어떠한 근거로 이렇게 나눴다 라는 말씀을 듣고 DDD원칙에 완전히 준수한 패키지 구성은 아니였지만 꼭 DDD에 걸맞게 패키지를 구성하는게 옳은 것은 아니구나, 역시 은탄환은 없구나 라는 것을 한번 더 느끼며 흥미롭게 시작했다. 테이블 설계역시 프로젝트의 시작은 테이블 설계이다. N:M 관계를 어떻게 풀어나가는지 학습해보자.대표적으로 프로젝트 테이블을 봐보자.(1) project <-> project_skill <-> skill 관계 분석현재 project_skill 테이블에는 다음 컬럼이 있다.project_id (FK) -> project의 project_id를 참조skill_id (FK) -> skill이 테이블은 다대다(N:M) 관계를 풀기 위한 "연결 테이블" 역할을 수행한다. 즉, 한 프로젝트가 여러 개의 기술(skill)을 가질 수 있고, 한 기술(skill)이 여러 개의 프로젝트에 속할 수 있는 관계이다.project_skill 테이블이 N:M 관계를 풀어주는 역할을 한다! 이런 식으로 N:M관계를 서로 1:N , M:1로 만들어주기 위해서는 양쪽의 PK값을 PK로 복합키로 가지고 있는 매핑 테이블이 필요하다.(2) project <-> project_detail 관계 분석project_detail 테이블에는 project_id가 FK로 존재.1:다(1:N) 관계이더. 즉, 하나의 프로젝트가 여러 개의 상세 정보를 가질 수 있음.이런 식으로 RDBMS에 대하여 어떻게 관계를 형성할 것인지 사전에 정확하게 설계하는 습관이 중요하다. 테이블은 운영 상에 정보를 바꾸는 것이 제약이 크기 때문에 신중하자. 깃과 친해지기깃 명령어는 빠르고 간결하게 내가 원하는 실행을 할 수 있게 도와준다. 이에 앞서 IDE에서 제공해주는 기본 GUI기능을 알아봤다.현재 깃 브랜치 정보 보는법View > Tool Windows > Git브랜치 이름 rename하는 법깃허브에 내 프로젝트를 올려두는 법Git > Manage Remote > 팝업창에 git url 입력 후 okcommit 친 것을 push 하는 법아까 View > Tool Windows > Git를 통해 인텔리 제이 하단에 뜬 브랜치 정보 우클릭 하고 push최종!!깃허브 화면 리프래쉬 해보면 프로젝트 올라감을 확인 가능엔티티 추출하기데이터베이스 설계한 내용을 토대로 jpa 엔티티 규격에 맞게 엔티티를 설계 했다.이 내용은 기본적인 jpa지식이 탑재된 내용이라 이론 정리는 생략하였다.미션깃 프로젝트 생성하기앞선 과정을 통해 깃레포트지로리를 작성했습니다.테이블 설계하기1:N 관계의 erp 시스템에 대한 편성된 예산에 대하여 지출하는 프로세스를 설계했습니다. 1주차 회고Keep (만족했고, 앞으로도 지속하고 싶은 부분)기초베이스가 역시 중요하다는 생각이 들었다. 개발을 하다보면 누군가는 되게 짧은 시간에 배우고 잘 하는 사람을 볼 수 있다. 그럴 떄 조급함을 느끼곤 했는데 그 사람의 과거 인생을 봐볼 필요가 있다. 짧은 시간이 단 3개월이라고 치면 그 사람은 짧은 시간 만에 배운게 아닌 지난 몇 년의 경험에 3개월의 지식을 얹었을 거라는 것이다. 개발은 3개월 배웠겠지만 개발자적인 사고방식을 10년했을 수도 있다는 것. 결국 이러한 기초지식이 모여서 나의 체급을 올려야 하는 점을 배운 부분에 만족한다. 전체적인 설계 및 개발 지식을 더 단단하게 배운 거 같아 벌써 앞으로의 여정이 기대됩니다.Problem (아쉬웠던 점)코틀린이라는 언어가 처음이라 다소 익숙하지 않은 문법들이 있다. 결국 자바와 하고 싶은 게 똑같다 보니 이해하는데 어려움이 있지는 않지만 코틀린 특유의 문법을 좀 더 깊게 이해하고 싶다는 생각이 들었다.Try (다음에 시도해볼 점)테이블 구조를 보고 올바른 테이블 설계인지 아닌지를 고민해보는 시간을 가지려고 한다. 결국 테이블 설계는 엔티티 설계에 영향을 주게 되고 프로젝트 전체의 흐름에 영향을 주다보니 중요한 요소인 거 같다.

백엔드코틀린워밍업클럽3기백엔드프로젝트

박소윤

[워밍업 클럽 3기 Backend 클린코드 & 테스트코드] 한 발자국 👣

이 글은 박우빈 강사님의 Readable Code: 읽기 좋은 코드를 작성하는 사고법 강의를 기반으로 인프런에서 진행하는 [워밍업 클럽 3기] Backend 클린 코드 & 테스트 코드 스터디 회고록입니다. 아래 학습 내용은 박우빈 강사님의 Readable Code: 읽기 좋은 코드를 작성하는 사고법 강의를 듣고 학습한 내용입니다.저작권 보호를 위해 강의 내용보다는 학습하면서 느낀 점 위주로 정리하였습니다. 정신없는 한 주를 보냈다. 하지만 그만큼 뿌듯한 한 주였다.본격적으로 [워밍업 클럽 3기] Backend 클린 코드 & 테스트 코드 스터디 1주차 회고에 들어가기 전에 워밍업 클럽 스터디가 무엇인지? 왜 참여하게 되었는지?에 대해 정리해보았다. 🤔[워밍업 클럽 3기] Backend 클린 코드 & 테스트 코드About [워밍업 클럽] Backend 클린 코드 & 테스트 코드 스터디워밍업 클럽은 실무에 필요한 지식을 쌓을 수 있는 지식공유자 주도 스터디 프로그램이다. 현직자 출신의 지식공유자가 본인의 강의를 바탕으로 직접 스터디를 이끌며 진행하는 방식이다.왜 신청했는가?1⃣ 평소에 관심이 많던 주제였기 때문처음 코딩을 배우기 시작했을 때부터 막연하게 "좋은 코드란 무엇인가?", "어떻게 하면 더 읽기 쉽고 유지보수하기 좋은 코드를 작성할 수 있을까?" 같은 고민이 있었다. 하지만 막상 공부하려고 하면 무엇부터 시작해야 할지 감이 잡히지 않았다.클린 코드와 테스트 코드는 중요하다는 걸 알고 있었지만, 실력이 부족하다고 느껴서 "아직 내 수준에서 공부할 내용이 아닐지도 몰라" 하며 미뤄왔다. 테스트 코드 역시 마찬가지였다. 테스트 코드를 작성하는 이유는 알겠지만, 어디까지 테스트해야 하는지, 어떤 방식으로 작성해야 하는지 막막했다.이번 스터디는 이런 고민을 해결할 수 있는 기회라고 생각했다. 체계적으로 학습하면서 클린 코드와 테스트 코드에 대한 개념을 확실히 정리하고, 실무에서 활용할 수 있는 능력을 기를 수 있을 거라 기대했다.2⃣ 강제성이 있는 환경이 필요했기 때문나는 스스로 의지가 약하다고 생각해서, 새로운 것을 공부할 때 강제성이 있는 환경을 만들어야 꾸준히 학습할 수 있다고 늘 느껴왔다. 그래서 항상 관심 있는 주제가 생기면 관련된 스터디나 프로젝트에 참여하면서 학습을 지속하려고 노력하는 편이다.이번에도 혼자서 클린 코드와 테스트 코드를 공부한다고 하면 분명 흐지부지될 가능성이 컸을 것이다. 하지만 스터디에 참여하면 일정에 맞춰 학습을 진행해야 하고, 함께하는 사람들이 있기 때문에 자연스럽게 동기부여도 될 것이라 생각했다.또, 단순히 강의를 듣는 것이 아니라, 여러 사람들과 함께 학습하고 피드백을 주고받을 수 있다는 점도 매력적이었다. 혼자 공부하면 잘 이해되지 않는 부분을 그냥 넘어가게 될 수도 있지만, 스터디에서는 서로의 코드를 리뷰하고 의견을 나누면서 더 깊이 있는 학습이 가능할 것 같았다.3⃣ 좋은 기회를 놓칠 이유가 없었기 때문비록 예상보다 큰 금액의 강의를 신청해야 하는 상황이었지만, 이 기회가 정말 좋은 기회라고 생각해 참여하기로 결심했다.혼자서 클린 코드와 테스트 코드를 공부하려면 어려운 부분도 많고, 체계적으로 배울 수 있는 기회가 쉽게 오지 않기 때문이다. 하루 이틀 고민하다가 마감 1시간 30분 전에 결정을 내리게 되었고, 한 달 동안 집중해서 실무에 필요한 지식을 배울 수 있다는 점에서 이 기회를 놓치면 안 된다고 확신했다. ✏1주차 학습 내용1주차에 학습한 내용은 크게 세 부분으로 나눌 수 있을 것 같다.1⃣ 추상과 구체Section 2에서는 추상화에 대해 학습했다.코드를 작성할 때 추상화라는 개념이 막연하게 느껴졌었는데, 강의를 들으며 우리는 이미 자연스럽게 추상화를 하고 있다는 점을 깨달았다. 인간은 사고할 때 자연스럽게 추상과 구체를 오가며 정보를 정리하고, 이는 코드에서도 동일하게 적용된다.첫 번째 미션에서 "추상과 구체의 예시"를 찾는 과정이 처음엔 어렵게 느껴졌지만, 결국 우리의 모든 행동과 사고 속에서 추상화가 이루어지고 있다는 것을 알게 되었다. 이 깨달음 덕분에 추상화가 어렵게만 느껴질 필요가 없다는 자신감을 얻었다.처음엔 "왜 이렇게까지 추상화에 대해 이야기하는 걸까?" 싶었지만, 결국 적절한 추상화가 곧 읽기 좋은 코드의 핵심 요소라는 것을 이해하게 되었다. 마치 글을 쓸 때 독자가 이해하기 쉬운 방식으로 핵심 정보만 전달하는 것이 중요하듯, 코드에서도 불필요한 복잡성을 줄이고 필요한 정보만 명확하게 담는 것이 중요하다. 따라서 가독성 좋은 코드를 작성하기 위해 추상화 능력을 기르는 연습이 필요하다는 점을 배울 수 있었다.2⃣ 코드 자체의 가독성 관점에서의 클린 코드Section 2와 Section 3에서는 코드 자체의 가독성 관점에서 클린 코드 작성 방법을 학습했다.이 과정에서 가장 중요한 포인트는 "개발자의 뇌 메모리를 덜 소모하게 만드는 코드"가 클린 코드다라는 개념이었다.흥미로웠던 점은, 내가 평소에도 코드 작성 시 사소한 요소 하나하나에 대해 고민을 많이 해왔다는 것이다. 변수 선언 위치, 공백 라인, early return 등의 요소들이 단순한 스타일 문제가 아니라 실제로 코드의 가독성과 유지보수성을 높이는 데 중요한 역할을 한다는 점을 배웠다.사실 지금까지도 코드를 작성하면서 "이렇게 작성하는 게 더 나을 것 같은데" 라는 막연한 느낌으로 스타일을 정리해왔고, 프로젝트를 진행할 때도 사소한 부분을 계속 신경 쓰면서 코드를 다듬었다. 하지만 명확한 근거 없이 단순히 직관적으로 판단하는 경우가 많아서, 다른 사람에게 수정 요청을 하거나 피드백을 주기가 어려웠다.그런데 이번 강의를 통해 내가 고민했던 부분들이 괜한 집착이 아니라, 실제로 코드의 가독성을 높이는 중요한 요소들이었다는 것을 확인할 수 있었다. 또한, 단순히 "이렇게 작성하는 게 더 좋아 보인다"가 아니라 왜 그렇게 작성해야 하는지에 대한 명확한 이유를 알게 되면서, 앞으로 코드 리뷰나 협업에서도 더 자신 있게 의견을 낼 수 있을 것 같다.과거에는 "너무 사소한 것에 신경 쓰는 건 아닐까?" 하는 고민이 있었지만, 이제는 가독성이 좋은 코드를 작성하기 위해 꼭 필요한 과정이었다는 확신을 가지게 되었다. 앞으로도 이런 고민을 계속하며 더 좋은 코드를 작성하기 위해 노력해야겠다고 다짐했다.3⃣ 객체 지향적 관점에서의 클린 코드Section 4와 Section 5에서는 객체 지향적 관점에서 클린 코드 작성 방법을 학습했다.나는 Java를 개념적으로 먼저 공부한 것이 아니라, 여러 프로젝트를 진행하면서 자연스럽게 배우는 방식으로 익혀왔다. 그러다 보니 객체 지향 개념(SOLID 원칙, 상속, 추상화, 다형성 등)에 대해 깊이 있게 이해하지 못한 채 적용하는 경우가 많았다. 개념을 따로 공부해도 실제 코드에서 이를 활용하는 것이 쉽지 않았고, 객체 지향 설계를 할 때도 원칙적으로 접근하기보다는 경험적으로 해결하는 경우가 많았다.그래서 이번 강의에서는 더욱 집중해서 학습하려고 했다. 강의가 개념 설명만 하는 것이 아니라, 실제 프로젝트에 적용해보는 방식으로 진행되어 단순한 이론이 아니라 실제 코드에 녹여내는 연습을 할 수 있었다.물론 한 번의 학습으로 모든 것을 완벽히 이해했다고 할 순 없지만, 앞으로 코드를 작성할 때 이러한 개념들을 고민하며 적용하는 습관을 들이면 자연스럽게 체득할 수 있을 것이라 생각한다. 특히, 이제는 단순히 프로젝트를 통해 Java를 익히는 것이 아니라, 객체 지향적 설계를 고민하면서 더 나은 코드 구조를 만들기 위한 연습을 병행해야겠다고 다짐했다.결국, 좋은 코드를 작성하기 위해서는 이론을 이해하는 것뿐만 아니라 꾸준한 연습과 적용이 필수적이라는 점을 다시 한번 깨달았다. 앞으로 더욱 노력해야겠다. 📜미션이번 주는 Day 2와 Day 4에 총 2개의 미션을 수행했다. 미션에 관련한 내용은 아래 링크에서 확인할 수 있다.Mission 1 | Day 2MISSION 1Mission 2 | Day 4MISSION 2 👍Keep: 잘한 점솔직히 말하면, 잘한 점보다는 아쉬운 점이 더 많이 떠오른다. 하지만 확실한 것은, 그 어느 때보다도 열심히 참여했다는 점이다.스터디 주제에 평소 관심이 많았고, 좋은 기회인 만큼 한 달 동안 최대한 많은 내용을 얻어가고 싶었다. 그래서 강의를 듣고 내용을 꼼꼼하게 정리하며 최선을 다해 공부했다. 이 점만큼은 스스로도 잘했다고 생각한다.👎Problem: 개선이 필요한 점너무 꼼꼼하게 공부하려다 보니, 강의에서 한 부분도 놓치고 싶지 않다는 마음이 강했다. 물론 꼼꼼한 학습이 나쁜 것은 아니지만, 다른 사람들보다 2~3배 더 많은 시간이 걸려 학습 속도가 느려졌다. 특히 수요일마다 일정이 있어 공부 시간이 부족한 날이 있었고, 그로 인해 학습이 조금씩 밀리는 점이 아쉬웠다.또한, 집중력이 부족하다고 스스로 느껴 힘들었다. 공부할 내용은 많은데 시간이 더 걸리다 보니 스트레스를 받았고, 결국 새벽까지 학습하는 상황이 반복되었다.🤙Try: 더 나은 결과를 위해 시도할 점앞으로는 모든 내용을 한 번에 완벽히 이해하겠다는 부담을 조금 내려놓으려 한다.만약 한 번에 이해되지 않더라도, 우선 평일에 진도를 끝내고 주말에 복습하는 방식으로 학습 패턴을 조정해볼 계획이다. 이를 통해 학습 효율을 높이고, 보다 균형 잡힌 공부 습관을 만들어 나가려 한다.다음 주부터는 이러한 방식을 적용해, 보다 효과적인 학습을 실천해볼 예정이다.

개발 · 프로그래밍 기타워밍업클럽워밍업클럽3기

박소연

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

🐾 발자국워밍업 클럽 3기 BE 클린코드 & 테스트 - 1주차를 듣고 작성하는 발자국입니다이번에 들은 강의 바로 가기 ⬇Readable Code: 읽기 좋은 코드를 작성하는 사고법 📃 회고[워밍업 클럽을 듣게 된 이유]대학교 개강을 앞두고 있던 와중 ,,, 개강하면 열심히 살아야겠다는 다짐을 하던 와중인프런 워밍업 클럽을 모집한다는 메일을 발견하게 되었습니다지금까지 인프런에서 강의를 완강하려면 거의 3~4개월이 필요했던 저는 이번 기회를 통해서 공부 습관을 다잡고 하루를 알차게 살아보자라는 생각에강의를 결제하고 워밍업 클럽을 신청하게 되었습니다. [워밍업 클럽 1주차를 들으며 .. ]박우빈 코치님이 짜주신 커리큘럼을 따라서 강의를 듣자는 목표를 세웠습니다.OT 를 참가했을 때 우리가 진행하는 커리큘럼이 굉장히 빡셀 수도 있다라는코치님의 말을 들으면서 내가 할 수 있을까 ,,,, 🧐 라는 생각도 잠시실제로 들어보니깐 힘들긴 했지만 그래도 못들을 정도의 학습량은 아니더라고요 !✔ 커리큘럼이 짜여져 있고✔ 강의를 듣는 사람들이 여러명이고✔ 미션을 통해 강의 내용을 중간중간 확인할 수 있고✔ 강의 내용이 좋아서더욱 열심히 들었습니다 !!이론으로 듣고 실제 코드를 고쳤을 때의 희열도 있었던 것 같아요 ㅎㅎ 👍 1주차에서 잘한 점 날마다 정해진 강의량을 들은 것미션을 빼먹지 않고 참여한 것 👎 1주차에서 못한 점강의를 듣고 난후 나만의 언어로 바꿔서 복습하지 않은 것 🥅 2주차의 목표강의를 모두 들은 후 나만의 언어로 정리해 내 걸로 만들기 !📺 강의 내용 정리섹션2 추상 (抽象)우리가 클린코드를 추구하는 이유 우리는 가독성을 위해서 클린 코드를 추구한다.가독성이 좋아지면 얻을 수 있는 것들이 많다. (이해가 잘되고, 유지보수하기 수월함)클린 코드를 추구하기 위해서 가장 중요한 주제가 바로 추상추상과 구체 중요한 정보는 가려내어 남기고, 덜 중요한 정보는 생략하여 버리는 것이 추상인간은 자연스럽게 추상화 능력을 가지고 있다'잘못된 추상화'는 굉장히 큰 사이드 이펙트를 가져오지만'적절한 추상화' 는 해당 도메인의 문맥 안에서 정말 중요한 핵심 개념만을 남겨서 표현함으로서 굉장히 큰 이점을 얻을 수 있다.이름 짓기이름을 짓는다는 건 추상적 사고를 기반으로 한다단수와 복수를 구분하기 / 이름 줄이지 않기 / 은어, 방어 사용하지 않기 / 좋은 코드를 보고 습득하기 메서드와 추상화잘 쓰여진 코드라면 한 메서드의 주제는 반드시 하나다.메서드의 이름은 구체적인 내용을 추상화해서 잘 나타내야 한다생략할 정보와 의미를 부여하고 드러낼 정보를 잘 구분해야 한다 메서드 선언부 반환타입 메서드명 ( 파라미터 ) 가 메서드 선언부반환타입, 메서드명, 파라미터에 추상화된 정보를 잘 담아야 한다추상화 레벨메서드로 추출한 경우 메서드 내부와 외부로 경계가 생긴다각각의 세계에서는 추상화 레벨이 동등해야 한다  섹션3 논리, 사고의 흐름뇌 메모리 적게 쓰기 최소한의 인지로 최대한의 효율을 내자뇌의 메모리에 적은 정보를 올릴수록 읽기 쉬운 코드가 된다Early returnif - else if - else 를 사용하는 경우 else 까지 오게 되면 If, else if 의 내용을 기억하고 있어야 한다이렇게 복잡한 코드를 사용하는 것이 아닌 조건문이 끝날 때마다 return 을 하게 되면 앞의 코드를 신경쓰지 않아도 된다사고의 depth 줄이기중첩 분기문, 중첩 반복문을 사용하면 사고의 depth 가 굉장히 깊어진다무조건 1 depth 로 만드는 것이 아니라 추상화를 통한 사고 과정의 depth 를 줄이는 것이 중요하다공백 라인을 대하는 자세공백 라인도 의미를 가진다복잡한 로직의 의미 단위를 나누어 보여줌으로써 읽는 사람에게 추가적인 정보를 전달할 수 있다부정어를 대하는 자세부정어구를 쓰지 않아도 되는 상황인지를 체크하고부정의 의미를 담은 다른 단어가 존재하는지 고민하거나 부정어구로 메서드명을 구성한다해피 케이스와 예외 처리사람은 해피 케이스에만 몰두하는 경향이 있지만 예외처리를 꼼꼼히 할수록 더욱 좋은 개발자가 된다예외가 발생할 가능성을 낮추고, 의도한 예외와 예상하지 못한 예외를 구분하자항상 NullPointException 을 방지하는 방향으로 개발해야 한다Optional 은 비싼 객체이기 때문에 꼭 필요한 상황에서 반환타입에 사용한다. 섹션 4 객체 지향 패러다임추상의 관점으로 바라보는 객체 지향 객체 지향 : 객체를 통해 객체간의 협력으로 프로그래밍 객체 : 추상화 된 [데이터 + 코드] 관심사를 분리해 관심사를 한 곳으로 모으면 유지보수에 도움이 된다객체 설계하기관심사가 퍼져 있는 코드에서 관심사를 한 곳으로 모다 객체로 분리해야 한다분리된 객체에서 공개 메서드를 통해서 기능을 제공한다객체의 책임이 나뉨에 따라 객체간의 협력이 강조된다새로운 객체를 만들때에는 1개의 관심사로 명확하게 책임이 정의되었는지 확인한다SRP (단일 책임 원칙)하나의 객체는 하나의 책임만 가져야 한다관심사를 분리해서 높은 응집도, 낮은 결합도OCP (개방-폐쇄 원칙)확장에는 열려있고, 수정에는 닫혀있어야 한다추상화와 다형성을 활용해 OCP 를 지킬 수 있다LSP (리스코프-치환 원칙)상속 구조에서 자식 클래스는 부모 클래스의 책임을 준수해야한다ISP (인터페이스 분리 원칙)클라이언트는 자신이 사용하지 않는 인터페이스에 의존해선 안된다DIP (의존성 역전 원칙)상위 수준의 모듈은 하위 수준의 모듈에 추상화로 의존해야 한다 섹션 5 객체 지향 적용하기상속과 조합상속보다는 조합을 사용해야 한다. 상속은 시멘트처럼 굳어지는 구조이기 때문에 수정이 어렵다Value Object 기본타입을 객체로 감싸서 의미를 부여하고 추상화하는 기법값으로 취급하기 위해서 불변성, 동등성, 유효성 검증 등을 보장해야 한다Entity 는 식별값으로 객체를 비교하지만, VO 는 내부의 모든 값이 같아야 같은 객체로 취급 ✔ 미션DAY2 미션 - 추상과 구체의 예시 미션내용 : "추상과 구체" 강의를 듣고, 생각나는 추상과 구체의 예시가 있다면 한번 3~5문장 정도로 적어봅시다. 미션 수행 내용추상스타벅스에서 음료를 테이크아웃한다구체스타벅스 앱에 접속한다주문할 매장을 선택한다픽업 옵션에서 To-go 를 선택한다슈크림라떼를 선택한 뒤, 원하는 옵션을 추가한다결제 방식을 선택한 후 결제를 완료한다매장에 도착해 주문 번호가 호출될 때까지 기다린다.슈크림라떼를 테이크아웃한다 DAY4미션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 boolean validateOrder(Order order) { if (order.isEmptyOrder()) { showInfoLog("주문 항목이 없습니다."); return false; } if (order.isNotHaveCustomerInfo()) { showInfoLog("사용자 정보가 없습니다."); return false; } if (order.isNotValidPrice()) { showInfoLog("올바르지 않은 총 가격입니다."); return false; } return true; } private void showInfoLog(String msg) { log.info(msg); }Order 객체 내부에 isEmptyOrder, isNotHaveCustomerInfo, isNotValidPrice 메서드를 만들어서 Order 에 대한 행동을 Order 자체에서 제어할 수 있도록 구현isEmptyOrder : 주문이 비었는지 아닌지 확인isNotHaveCustomerInfo : 사용자 정보가 비었는지 확인isNotValidPrice : 올바르지 않은 총 가격인지 확인 DAY4미션2 - SOLID미션 내용 : SOLID에 대하여 자기만의 언어로 정리해 봅시다.미션 수행 S (단일책임원칙)각 객체가 하나의 명확한 관심사(책임)만을 가져야 한다책임을 발견해내는 것은 경험의 영역이기 때문에 객체를 설계할 때마다 현재 객체가 하나의 책임만 가지고 있는지 질문해야 한다ex) MinesweeperGame -> Minesweeper 랑 GameApplication 으로 분리O (개방-폐쇄원칙)확장에는 개방되어 있고, 수정에는 폐쇄되어야 한다새로운 요구사항이 발생되었을 때에도 기존 코드를 직접 수정하지 않고, 확장할 수 있는 방식으로 설계해야 한다ex) 결제 수단을 여러 개 추가해야 하는 경우L (리스코프 치환 원칙)상속구조에서 부모클래스를 자식클래스로 치환할 수 있어야 한다부모클래스에서 사용하는 기능을 자식클래스가 사용하지 않는다면 추가적인 코드 처리가 필요하게 되는데, 이런 경우 사이드 이펙트가 발생할 수 있다ex) Animal 클래스에 fly 메서드가 있는데 Animal 을 상속받는 동물이 사자인 경우I (인터페이스 분리 원칙)인터페이스의 단위를 잘게 쪼개 클라이언트가 사용하지 않는 기능을 사용하지 않도록 해야 한다인터페이스에 클라이언트가 사용하지 않는 기능이 포함되어 있으면 사용하지 않는데 추가적인 코드 작업을 해야하고, 클라이언트 입장에서는 해당 메소드의 의미를 모르게 된다ex) Animal 인터페이스에 fly() 랑 walk() 메서드가 있으면 Fly 인터페이스, Walk 인터페이스 로 분리D (의존성 역전 원칙)상위 수준 모듈은 하위 수준 모듈의 추상화에 의존해야 한다상위 수준 모듈이 하위 수준 모듈의 구체적인 코드를 알 필요가 없다. 추상화를 통해 어떤 기능을 제공하는지만 알려주고 내부적인 코드는 하위 수준 모듈들에서 작성할 수 있도록 해야 한다상위 수준 모듈인 음식점의 경우 하위 수준 모듈로 라면끓이는 법을 아는게 아니라 음식만드는 법이라는 추상화에 의존하면 여러 메뉴를 만들 수 있음

백엔드워밍업클럽워밍업클럽3기클린코드&테스트

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

⭐️ 박우빈님의 Readable Code: 읽기 좋은 코드를 작성하는 사고법 강의를 수강하며 작성한 내용입니다.  강의 수강명확한 이름 짓기추상화 레벨 맞추기빨리 리턴하기추상화를 통해 중첩문 줄이기공백의 의미부정어보다 긍정어/바로 읽히도록예외 가능성 낮추기Setter 금지, Getter는 생각해보고 쓰기필드 수는 적게 사용하기도메인 지식은 만드는 것이 아니라 발견하는 것 미션Day2카페에서 커피를 마시는 것을 구체 레벨로 표현하기 카페에 도착한다.원하는 음료를 키오스크 혹은 직원에게 주문한다.‘나’는 자리에 앉는다.직원이 음료를 제조한다.제조한 음료를 ‘나’에게 전달한다.전달받은 음료를 가지고 자리에 돌아온다.음료를 마신다.Day4https://night-geography-507.notion.site/3-BE-Day-4-1af205648a1680e78f9ade8e629bdc6a  회고미리 듣는 건 좋았지만 하루 이틀 미뤄지다보니 온전히 집중해서 수강하지 못한 점이 아쉽다.미션을 통해 추상화를 직접 적용해보고 SOLID에 대해서도 내 생각을 정리해 볼 수 있어서 흥미로웠다.목표다음주는 꼭 주어진 챕터를 그날그날 듣고 끝내는게 목표!미리 하겠다는 욕심에 더 미루지 말자무조건 따라하기 보다 왜 이렇게 했는지 생각해보기좋은 건 실무에 바로 적용해서 내 걸로 만들기

백엔드워밍업클럽워밍업클럽3기박우빈백엔드백엔드스터디

LC-02s

[인프런 워밍업 클럽 3기] 풀스택 스터디 1주차 미션 회고 발자국

학습 내용 요약인프런 워밍업 클럽 3기 풀스택 스터디 1주차에는 해당 강의에서 사용될 라이브러리 및 프레임워크에 대한 소개와 사용법 위주로 다루었습니다. 인프런에서 발자국을 작성할 때 강의를 보지 않고도 강의 내용을 파악할 수 있을 만큼 자세한 내용을 작성하는 건 지양해 달라고 가이드한 만큼 강의에 대한 필기는 최소화 하고 회고 위주로 적어보겠습니다. 강의의 목적인 Next.js와 Supabase를 사용해서 서비스를 만들어보는 것에 집중하여 스타일링이나 기타 상태관리 라이브러리에 대한 내용은 작성하지 않았습니다. Next.js 간단 정리Next.js란?개인이 풀스택 개발을 하기에 최적화 된 웹 프레임워크서버사이드 렌더링이 큰 특징이며, React와 비슷한 문법으로 더 현대적인 웹 서비스를 구현할 수 있음서버에서부터 HTML을 최적화해서 웹으로 내려주기 때문에 SEO에 도움이 됨자체적으로 API 구축이 가능하기 때문에 사이즈가 크지않은 개인 프로젝트 정도의 규모라면 별도로 서버구축을 할 필요 없이 Next.js만으로 서버 구축까지 전부 가능함Server Actions서버에서 실행되는 비동기 함수 생성 기능"use server" 지시어를 파일 최상단에 선언하면 해당 파일 내 모든 함수가 Server Action으로 간주됨서버 및 클라이언트 컴포넌트에서 호출 가능주로 폼 제출 및 데이터 변경(뮤테이션)에 사용내부적으로 POST 요청을 사용하며, 인자 및 반환값은 React에서 직렬화 가능해야 함 Supabase 간단 정리Supabase 란?PostgreSQL 기반의 오픈소스 백엔드 서비스로, 개발자들이 손쉽게 데이터베이스, 인증, 스토리지, 서버리스 함수 등을 활용할 수 있도록 지원하는 플랫폼상용 서비스인 Firebase의 대안으로 자주 언급됨Supabase 장점오픈소스 프로젝트 (자체 서버 구축 가능)PostgreSQL 기반 (관계형 DB 장점을 살릴 수 있다)Firebase 대비 저렴다양한 연동 방식 지원 (+ GraphQL, API, SDK, DB Connection)Supabase 단점아직 성숙하지 않은 커뮤니티 기반비교적 적은 기능들, 적은 서비스 연동 지원부족한 문서화, 한글 문서 부족Firebase보다 높은 러닝커브  Todo List 미션 회고풀스택 스터디 1주차 미션은 강의에서 진행하는 Next.js와 Supabase를 활용한 Todo List 앱에 기능을 더 추가해보는 것이었습니다. 진행했던 미션은 해당 링크에서 보실 수 있습니다. 미션 수행 내용아래는 제가 수행한 미션에 대한 내용과 함께 고민했던 점과 아쉬웠던 점을 정리해보았습니다. 기능 명세할 일 목록 조회 기능키워드 검색 기능할 일 수정 기능내용 변경 기능완료 여부 변경 기능할 일 삭제 기능정리해보면 위의 기능을 Next.js와 Supabase를 활용하여 구현하는 것이었습니다. 강의에서는 Material Tailwind 라이브러리를 사용했지만 저는 기존에 즐겨썼었던 TailWindCSS와 최근 관심가지게된 Mantine 라이브러리를 사용하여 UI를 구축하였고, 4주차 모두 동일한 환경으로 진행되는 것 같아 Turbo Repo와 pnpm workspace를 활용한 모노레포 환경을 구축해두고 프리셋을 관리하였습니다. 상세한 내용은 아래에 정리해두었습니다. 사용 기술프레임워크: Next.js v15, React v19데이터베이스: Supabase서버 상태관리: Tanstack Query v5클라이언트 상태관리: Zustand v5스타일 프레임워크: TailWindCSS v3, Mantine v7모노레포: Turbo Repo패키지 매니저: pnpm 강의에서는 TypeScript를 사용했지만 별도의 인터페이스를 작성하지는 않았는데, 저의 최근 관심사가 환경(라이브러리) 변화에 유연한 도메인 로직 작성하기여서 저는 아래와 같이 별도로 인터페이스를 작성 후 서버 액션을 사용하는 로직을 인터페이스에 맞추어 작성해 보았습니다. export interface Todo { id: number title: string createdAt: string updatedAt: string completed: boolean completedAt: string | null }export interface GetTodoListParams { query?: string } export interface GetTodoList { (params: GetTodoListParams): Promise<Todo[]> }export type CreateTodoParams = Pick<Todo, 'title'> export interface CreateTodo { (params: CreateTodoParams): Promise<void> }export type UpdateTodoParams = Pick<Todo, 'id' | 'title' | 'completed'> export interface UpdateTodo { (params: UpdateTodoParams): Promise<void> }export type DeleteTodoParams = Pick<Todo, 'id'> export interface DeleteTodo { (params: DeleteTodoParams): Promise<void> } 기존에는 서버 액션에 대한 이해가 부족했어서 항상 Next.js를 사용하며 Route Handler로 별도의 API를 만들어서 작업했었는데 별도의 API 엔드포인트를 관리하지 않고 서버 액션으로 모두 구축해볼 수 있었습니다. 이번에는 강의에서 진행한 것과 같이 GET 요청에도 서버 액션을 사용했었는데 개인적으로 관련 내용에 대한 고민을 조금 했었습니다. Next.js의 공식 문서에서는 일반적으로 서버 액션을 데이터 변경(뮤테이션) 로직에만 사용하는 것으로 소개되었기도 했고, 기본적으로 POST 메서드를 사용한다고 했었는데, 이렇게 GET, PUT, DELETE 메서드를 사용해야하는 로직도 모두 POST로 통합해도 되는가라는 의문이 하나 있었고, GET 요청에도 서버 액션을 사용하니 생긴 문제인데 Next.js에서의 캐싱 전략을 어떻게 사용할 수 있을까에 대한 의문이 있었습니다. 강사님께서 제시하는 생산성 높은 풀스택 개발 방법론은 Supabase로 백엔드 필요 기능을 쉽고 빠르게 구축하고 Next.js의 서버 액션을 활용하여 별도의 API를 관리하는 절차를 건너뛰는 것이라고 이해했습니다. 저도 1인 풀스택 개발에 뜻이 있는 입장에서 되게 괜찮은 방법론이라 생각했는데, 해당 의문은 이로인해 근본적으로 서버 액션이라는 기술의 활용 범위를 넓힘으로 인해 발생했다고 생각합니다. 첫 번째 의문은 Next.js의 특수성으로 미루어 보아 문제가 없다고 결론지었습니다. 서버 액션이라는 개념 자체가 일반적으로 REST API를 설계하는데 사용되는 개념이 아니기도 하고, 애초에 엔드포인트 자체도 루트 경로로 받기 때문에 의미가 없는 것이죠. 프레임워크가 관리해주기 때문에 메서드와 엔드포인트로 작업 단위를 표현하지 않아도 괜찮다고 생각했습니다. 두 번째는 의문이라기 보다는 문제였죠. 저는 해당 문제를 해결하기 위해 Tanstack Query를 사용했습니다만 근본적인 해결은 아니었습니다. HydrationBoundary 와 prefetchQuery를 활용하여 SSR과 서버 컴포넌트 환경을 통합하였고, useQuery 훅과 Query Key Factor 방식을 사용하여 캐싱 전략 관리를 시도했는데, 다 만들고 보니 이건 클라이언트에 해당하는 캐싱 전략이었어요. 서버 쪽에서는 적절한 캐싱 전략이 없었습니다. 해당 문제는 공식 문서를 찾아보니 라우트 세그먼트의 설정을 상속받아 maxDuration 같은 설정을 따른다고 안내되고 있었습니다. 적용해보지는 못했지만 page.tsx에서 결정할 수 있는 것 같아 아마 다음 미션에서 기회가 된다면 사용해볼 예정입니다. 벌써 다음 미션이 기대되네요. 긴글 읽어주셔서 감사합니다. ☺

프론트엔드워밍업클럽3기풀스택Next.jsSupabase

[워밍업 클럽 3기 - CS 전공 지식] - Day 5 미션 1

운영체제 1. while(true){ wait(1); // 1초 멈춤 bool isActivated = checkSkillActivated(); // 체크 } 위 코드는 1초 마다 플레이어가 스킬을 사용했는지 체크하는 코드입니다. 이 방식은 폴링방식입니다. 1초마다 체크하기 때문에 성능에 좋지 않습니다. 이를 해결하기 위한 방식으로 어떤 걸 이용해야 할까요? A. 인터럽트(Interrupt) 방식 프로그램과 프로세스가 어떻게 다른가요?A. 프로그램 자체는 명령어의 집합일 뿐이다. 반면에 프로세스는 해당 프로그램을 실행하여, 프로세스가 사용할 독립된 메모리 공간을 할당받는다. 멀티프로그래밍과 멀티프로세싱이 어떻게 다른가요? A. 멀티 프로그래밍은 메모리에 여러개의 프로세스를 올려서 처리하는 방식이다. CPU가 한 프로그램의 I/O 작업 등을 기다리는 동안 다른 프로그램을 실행할 수 있게한다. 멀티 프로세싱은 여러 CPU(또는 코어)를 사용하여 여러 프로세스를 실제로 동시에 처리하는 것을 말한다. 운영체제는 프로세스를 관리하기 위해서 어떤 것을 사용하나요? A. 프로세스를 관리하기 위한 주요 요소는 다음과 같다.프로세스의 정보를 담고있는 PCB(Process Control Block)를 사용생성, 준비, 대기, 실행, 완료로 프로세스 상태를 관리한다어떤 프로세스에 CPU 시간을 할당할지 결정하기 위해 CPU 스케쥴링(Scheduling)을 사용한다  컨텍스트 스위칭이란 뭔가요?A. 운영 체제가 CPU를 하나의 프로세스에서 다른 프로세스로 전환하는 과정. 과정을 대략적으로 설명하면 다음과 같다.인터럽트가 발생하면, 운영 체제는 현재 실행 중인 프로세스의 상태 정보를 PCB에 저장(스케줄러는 준비 상태 큐에 있는 프로세스 중에서 선택)선택된 프로세스의 PCB에서 상태 정보를 참조해서 상태를 복원  자료구조와 알고리즘  여러분은 교실의 학생 정보를 저장하고 열람할 수 있는 관리 프로그램을 개발하려고 합니다. 이 때 여러분이라면 학생의 정보를 저장하기 위한 자료구조를 어떤 걸 선택하실 건가요? 이유를 함께 적어주세요. A1. 단일 교실 규모라면 배열 또는 리스트를 사용한다이유: 학생에 대한 변경(추가/삭제)는 자주 일어나지 않는다. 순서를 보장할 수 있다. 교실 전체의 학생을 순회하여 조회하는 경우가 많다.A2. 학교 전체를 관리하는 경우 해시 맵을 사용한다이유: 읽기/쓰기/삭제 등의 작업에 O(1)의 성능을 보장하기 때문에 효율적이다. 현재 컴퓨터의 성능을 고려하면, 메모리 문제가 발생할 가능성은 낮다. 특정 정보를 키로 활용할 수 있다.(예시: 학번을 키로 사용) 여러분은 고객의 주문을 받는 프로그램을 개발하려고 합니다. 주문은 들어온 순서대로 처리됩니다. 이 때 여러분이라면 어떤 자료구조를 선택하실 건가요? 이유를 함께 적어주세요.A. 큐(Queue) 자료구조를 사용한다이유: 가장 먼저 들어온 데이터가 가장 먼저 나가는 구조이기 때문에, 주문이 들어온 순서대로 처리하기에 적합하다. 우리가 구현한 스택은 0번 인덱스, 즉 입구쪽으로 데이터가 삽입되고 나오는 구조입니다. 반대로 마지막 인덱스, 즉 출구쪽으로 데이터가 삽입되고 나오는 구조로 코드를 변경해주세요.A.push(data) { // 0번 인덱스 대신 마지막 인덱스에 삽입 this.list.insertLast(data); } pop() { try { // 0번 인덱스 대신 마지막 노드 삭제 및 반환 return this.list.deleteLast(); } catch(e) { return null; } } 해시테이블의 성능은 해시 함수에 따라 달라집니다. 수업 시간에 등번호를 이용해 간단한 해시 함수를 만들어봤습니다. 이번엔 등번호가 아닌 이름을 이용해 데이터를 골고루 분산시키는 코드로 수정해주세요. 힌트: charCodeAt() 함수를 이용 예시: name1 = "이운재"; name1.charCodeAt(0); // 51060 이운재의 0번 인덱스 ‘이’의 유니코드 출력 hashFunction(name) { let hashValue = 0; for (let i = 0; i < name.length; i++) { hashValue += name.charCodeAt(i); } return hashValue % 10; }

워밍업클럽3기CS미션

[워밍업 클럽 3기 - CS 전공 지식] - 1주차 발자국

✍ 학습 내용 복습Q. 배열(Array)의 특징은?A. 가장 기본적인 자료구조배열은 같은 종류의 데이터들이 순차적으로 저장되어 있다배열의 크배열은 메모리 주소가 연속될 것을 요구하기 때문에 배열의 크기를 늘리는 것은 불가능기에 상관 없이 인덱스를 알고 있으면 해당 원소로 접근하는데 걸리는 시간은 O(1)​이다(참조의 성능이 좋다)배열의 크기를 늘릴 필요가 있다면 크기가 더 큰 새로운 배열을 생성하여 기존 배열의 내용을 복사하는 과정이 필요하다 Q. 연결 리스트(Linked List)의 특징은?A.연결 리스트는 낭비되는 메모리 없이 필요한 만큼만 메모리를 확보해서 사용하기 위해서 노드를 만들고 각 노드를 서로 연결해서 사용한다노드는 저장할 데이터와 다음 노드로 향하는 참조를 가지고 있다. 첫 노드(헤드 노드)의 주소만 알고 있으면, 다른 연결된 모든 노드에 접근할 수 있다초기에 크기를 정해야 하는 기존 배열의 단점을 해결할 수 있다 Q. 해시 테이블(Hash Table)의 특징은?A.키-값(Key-Value) 형태의 자료구조키를 알고 있다면, O(1)의 성능으로 값을 읽기/삽입/수정/삭제하는 것이 가능하다(해시 충돌이 없다는 가정)해시 함수(해시 알고리즘)를 사용한다. 해시 함수를 통해 값에 대한 해시값을 계산한다.해시 인덱스(hash index)는 데이터가 저장될 위치라고 생각하면 편한다해시 값에 대한 해시 인덱스를 구하는 과정을 해싱(hashing)이라고 한다해시 충돌(hash collision)은 이미 키에 값이 존재하는 경우에 값을 삽입되는 경우 발생한다. 이 경우 해당 키에서는 값들을 리스트로 저장해서 사용한다. Q. 프로세스(Process)란?A. 실행 중인 프로그램. 프로그램 자체는 명령어의 집합일 뿐이다. 반면에 프로세스는 해당 프로그램을 실행하여, 프로세스가 사용할 독립된 메모리 공간을 할당받는다. Q. 시분할 시스템(Time-Sharing System)이란?A. CPU가 여러 프로세스에게 짧은 시간 간격(시간 조각)을 할당하고, 각 프로세스는 이 시간 동안만 실행되는 방식의 시스템. 시간 간격이 매우 짧아서 여러 프로세스가 동시에 실행되는 것처럼 보이게 만들 수 있다. Q. 컨텍스트 스위치(Context-switch)란?A. 운영 체제가 CPU를 하나의 프로세스에서 다른 프로세스로 전환하는 과정. 과정을 대략적으로 설명하면 다음과 같다.인터럽트가 발생하면, 운영 체제는 현재 실행 중인 프로세스의 상태 정보를 PCB에 저장(스케줄러는 준비 상태 큐에 있는 프로세스 중에서 선택)선택된 프로세스의 PCB에서 상태 정보를 참조해서 상태를 복원CPU가 새 프로세스를 실행하기 시작 🤔 회고CS 지식을 복습할 수 있어서 너무 좋았다

시스템 · 운영체제워밍업클럽3기CS운영체제자료구조

채널톡 아이콘