🎁[속보] 인프런 내 깜짝 선물 출현 중🎁

블로그

주이

[워밍업 클럽 3기] CS 2주차 - 발자국

재귀어떠한 것을 정의할 때 자기 자신을 참조하는 것재귀적으로 정의된 함수 → 재귀함수 콜스택함수가 호출되면서 올라가는 메모리 영역 → 스택이라고도 부름First In Last Outfor문으로 처리할 수 있는 작업을 재귀함수로 처리한다면 더 비효율적인 상황이 생김→ 재귀함수가 더 많은 메모리 공간 차지대신, 재귀함수를 사용하면 처리하기 쉬운 예도 있음. ex) 팩토리얼패턴 1. 단순 반복 실행반복문 구현보다 큰 이점은 없음. 콜스택 공간을 많이 차지해 오히려 성능 저하패턴 2. 하위 문제 결과를 기반으로 현재 문제 계산예) 팩토리얼, 하노이탑for 문을 이용한 계산 - 상향식 계산재귀를 이용해 하위 문제 계산 - 하향식 계산 (상향식도 구현 가능하지만 성능 발휘는 딱히 X)// 재귀를 이용한 상향식 계산 function factorial2(number, i = 1, sum = 1) { if (i > number) return sum; return factorial2(number, i + 1, sum * i); }  정렬 알고리즘버블 정렬(Bubble Sort)앞에 있는 숫자와 옆에 숫자를 비교해서 자리를 바꾸는 알고리즘장점: 이해 및 구현 간단단점: 성능이 좋지 않음성능 - O(n^2)(n-1) + (n-2) + (n-3) … + 2 + 1 → 등차수열n(n-1)/2 → (n^2 - n)/2 선택 정렬(Selection Sort)배열의 정렬되지 않은 영역의 첫 번째 원소를 시작으로 마지막 원소까지 비교 후 가장 작은 값을 첫 번째 원소로 가져옴장점: 이해 및 구현 간단단점: 성능이 좋지 않음성능 - O(n^2)(n-1) + (n-2) + (n-3) … + 2 + 1 → 등차수열n(n-1)/2 → (n^2 - n)/2

팥우유

[인프런 워밍업 클럽 Full Stack 3기] 2주차

1. 학습 내용1.1. Supabase Storage파일과 이미지를 저장하고 관리하기 위한 서비스 (아마존 S3와 유사)1.1.1. 주요 개념버킷(Buckets): 파일을 논리적으로 구분하는 컨테이너객체(Objects): 저장된 개별 파일정책(Policies): 파일에 대한 접근 권한 규칙 2. 미션2.1. 미션 내용파일 목록에서 각 파일의 "마지막 수정 시간"을 표시2.2. 미션 진행- 리액트 쿼리로 요청, 응답 받은 이미지 데이터 객체 내 update_at 키-값을 이미지 컴포넌트에 표시함으로써 작업을 진행했습니다.3. 추가 학습 및 적용 기술3.1. pnpm 도입과 경험npm의 문제점(패키지 중복 설치, 디스크 낭비)에 불편함을 느껴 이번 기회에 pnpm을 도입했습니다. 설치 속도가 확연히 빨라진 것을 체감할 수 있었습니다.3.1.1. npm과 pnpm의 주요 차이점저장 구조와 디스크 사용량npm: 프로젝트마다 의존성 중복 저장으로 디스크 낭비pnpm: 전역 저장소에 패키지를 한 번만 저장하고 심링크로 연결하여 공간 절약성능npm: 중복 다운로드로 인한 네트워크 부하 및 시간 소요pnpm: 공유 저장소와 링크 기반 구조로 설치 속도 향상, 병렬 처리 최적화3.2. material-tailwind 경고 제거material-tailwind 컴포넌트 사용 시 발생하는 경고 메시지를 d.ts 파일을 통해 제거했습니다. 이 방법이 안전한지 고민했지만, 큰 문제가 없다고 판단하여 적용했습니다. 인프런 커뮤니티의 질문과 답변이 해결에 도움이 되었습니다.3.3. supabase storage type 적용db type과 달리 storage type은 supabase cli를 통해 자동 생성할 수 없다는 점을 알게 되었습니다. 대신 `@supabase/storage-js` 플러그인을 통해 필요한 type을 활용할 수 있었습니다.// UploadedImage Component import { FileObject } from '@supabase/storage-js'; // ...existing code... export default function UploadedImage({ file: { name, updated_at }, }: { file: FileObject; }) { return (// component) }3.4. prettier 설정GitHub Copilot의 도움을 받아 Next.js 프로젝트에 적합한 옵션으로 설정했으며, prettier-plugin-tailwindcss를 통해 Tailwind 클래스 자동 정렬 기능을 추가했습니다.// .prettierrc { "singleQuote": true, "semi": true, "useTabs": false, "tabWidth": 2, "trailingComma": "es5", "printWidth": 80, "arrowParens": "avoid", "jsxSingleQuote": false, "bracketSpacing": true, "bracketSameLine": false, "htmlWhitespaceSensitivity": "css", "requirePragma": false, "insertPragma": false, "proseWrap": "preserve", "endOfLine": "auto", "plugins": [ "prettier-plugin-tailwindcss" ] }3.5. eslint 설정@tanstack/eslint-plugin-query를 도입하여 React Query 사용 시 모범 사례를 따르도록 설정했습니다. 이를 통해 쿼리 키 검증, 의존성 확인 등의 이점을 얻을 수 있었습니다.// .eslint.json { "plugins": ["@tanstack/query"], "extends": ["next/core-web-vitals", "plugin:@tanstack/query/recommended"] } 4. 아쉽게 적용하지 못한 기술아래 3 가지 항목들은 모두 조사, 기능 개발 계획, 프로젝트에 일부 적용까지 하기도 했으나 시간이 부족해 결국 완성되지 못한 기능들입니다.4.1. 한글 파일명 업로드 문제 해결Supabase Storage에서 한글 이름의 파일을 업로드할 수 없는 문제에 직면했습니다. 원인은 파일명 인코딩 과정이 없어서 발생한 문제였습니다. 두 가지 해결책을 구상했지만 시간 부족으로 완성하지 못했습니다.파일정보 DB 테이블 접근법: 파일명과 UUID를 매핑하여 DB에 저장하고, 실제 스토리지엔 UUID로 업로드하는 방식customMetadata 활용: Supabase Storage의 메타데이터 기능을 활용하는 방식4.2. 직접 Tailwindcss 컴포넌트 구현 시도material-tailwind 대신 Tailwindcss만으로 모든 컴포넌트를 스타일링해보고 싶었으나, 시간 부족으로 실현하지 못했습니다.4.3. 컴포넌트 구조 설계의 고민개발자 관점에서 명확하고 구분하기 쉬운 파일 구조를 만들기 위해 다양한 React 컴포넌트 구조 패턴을 조사했습니다.각 패턴의 장단점을 분석하며 프로젝트에 가장 적합한 구조를 고민했지만, 시간 관계상 실제 적용은 제한적이었습니다.제가 찾아본 React 주요 패턴들은 다음과 같습니다. 이해를 돕기 위해 각 패턴마다 특징 및 예시 코드까지 준비하였으나 글이 너무 길어져 읽는데 어려움이 있을 것 같아 최종적으로 간단하게 한 줄로 요약했습니다.4.3.1. Presentational and Container Pattern로직과 UI를 분리하는 패턴으로, 재사용성과 테스트 용이성이 향상되지만 props drilling 문제가 발생할 수 있습니다.4.3.2. Compound Component Pattern복합적인 UI를 구성하는 관련 컴포넌트들을 그룹화하고 내부적으로 상태를 공유하는 패턴입니다. API 사용 경험은 향상되지만 TypeScript 타입 정의가 복잡해질 수 있습니다.4.3.3. Render Props Pattern컴포넌트의 렌더링 로직을 prop 함수로 전달하는 방식으로, 로직 재사용은 용이하지만 콜백 중첩으로 인한 디버깅 어려움이 있을 수 있습니다.4.3.4. Custom Hook Pattern로직을 훅으로 추출하여 여러 컴포넌트에서 재사용하는 패턴입니다. React의 핵심 패턴 중 하나로, UI와 로직의 분리가 명확합니다.4.3.5. Context API Pattern여러 컴포넌트에서 데이터를 공유하기 위한 패턴으로, props drilling을 방지할 수 있지만 불필요한 리렌더링이 발생할 수 있습니다.4.3.6. Atomic Design PatternUI 컴포넌트를 원자(Atoms), 분자(Molecules), 유기체(Organisms), 템플릿(Templates), 페이지(Pages)로 나누는 구조로, 체계적인 UI 구성이 가능하지만 초기 설계 시간이 많이 소요됩니다.4.3.7. Client/Server Component PatternNext.js 14 App Router의 핵심 패턴으로, 서버에서 데이터를 페칭하고 클라이언트에서 인터랙션을 처리하여 번들 크기를 최적화합니다.4.3.8. Server Components and Suspense Pattern데이터 로딩 상태를 선언적으로 처리하는 패턴으로, 점진적 UI 로딩을 지원하고 사용자 경험을 향상시킬 수 있습니다. 5.마무리이번 스터디를 통해 많은 것을 배우고 적용해보는 즐거움을 느꼈습니다. 계획했던 것보다는 적게 구현했지만, 새로운 기술들을 탐색하고 실험해본 경험은 매우 가치 있었습니다. 더 많은 공부 시간을 확보하니 다양한 시도를 해볼 수 있었지만, 동시에 욕심이 커져 모든 계획을 실현하지는 못했습니다. 3주 차에는 개인 약속으로 인해 학습 시간이 줄어들 것 같지만, 지금까지의 경험을 바탕으로 더 효율적으로 학습하고 구현해보겠습니다.무엇보다, 호기심을 가지고 새로운 기술을 탐색하고 적용해보는 과정 자체가 개발자로서 성장하는 중요한 발판이 된다는 것을 다시 한번 느낄 수 있었습니다. 

jinwoo2511

인프런 워밍업 클럽 스터디 3기 - CS 전공지식 <2주차 발자국>

'그림으로 쉽게 배우는 운영체제 2주차'[섹션 04]프로세스 동기화CPU 스케줄링에서 고려해야 할 사항CPU 스케줄러가 고려해야 할 요소는 다음과 같습니다.프로세스에게 CPU 리소스를 줘야 하는가?실행 가능한 프로세스 중에서 어떤 프로세스에 CPU를 할당할 것인지 결정.우선순위가 높은 프로세스를 먼저 실행할 것인지 고려해야 함.CPU를 할당받은 프로세스가 얼마 동안 실행되어야 하는가?특정 프로세스가 너무 오랫동안 CPU를 독점하지 않도록 타임 퀀텀(Time Quantum) 을 설정.선점형(Preemptive) 스케줄링과 비선점형(Non-preemptive) 스케줄링 방식 중 선택.CPU Burst와 I/O BurstCPU Burst: 프로세스가 연속적으로 CPU에서 실행되는 시간.I/O Burst: 프로세스가 I/O 작업을 수행하는 시간.CPU 작업과 I/O 작업이 번갈아 가며 실행됨.프로세스 간 통신 (IPC, Inter-Process Communication) 종류파일과 파이프(Pipe) 이용파일: 프로세스 간 데이터를 파일을 통해 공유하는 방식.파이프: 한 프로세스가 데이터를 쓰고 다른 프로세스가 읽는 구조 (ex. | 연산자).스레드(Thread) 이용같은 프로세스 내의 여러 스레드가 메모리를 공유하면서 데이터를 주고받는 방식.네트워크 이용소켓(Socket) 통신, 원격 프로시저 호출(RPC) 등을 사용하여 원격 프로세스와 데이터 교환.RPC (Remote Procedure Call, 원격 프로시저 호출)네트워크를 통해 다른 시스템에서 동작하는 프로세스의 함수를 호출하는 기법.클라이언트가 마치 로컬 함수처럼 호출하면, 원격 서버에서 실행되고 결과가 반환됨.공유 자원 & 동기화 문제공유 자원(Shared Resource): 여러 프로세스가 동시에 접근할 수 있는 자원 (예: 변수, 파일).동기화 문제(Synchronization Issue): 여러 프로세스가 공유 자원에 접근할 때 데이터 충돌이 발생할 위험이 있음.임계구역 (Critical Section)공유 자원에 접근하는 코드 영역.한 번에 하나의 프로세스만 임계구역에 접근해야 함.상호 배제(Mutual Exclusion) 매커니즘 요구사항 3가지단일 접근 원칙: 주어진 시간에 오직 하나의 프로세스만 임계구역에 접근 가능.동시 요청 처리: 여러 프로세스가 동시에 요청해도 한 개의 프로세스만 진입 가능.빠른 실행 보장: 임계구역에 들어간 프로세스는 최대한 빠르게 나와야 함.세마포어 (Semaphore)공유 자원의 접근을 제한하는 동기화 기법.프로세스 간 상호 배제(Mutual Exclusion) 를 보장함.세마포어 메커니즘P(S) (wait 연산): 세마포어 값을 감소시키고, 0이면 대기(block).V(S) (signal 연산): 세마포어 값을 증가시키고, 대기 중인 프로세스가 있으면 깨움.C++ 예제#include <iostream> #include <semaphore.h> #include <pthread.h> sem_t semaphore; void* worker(void* arg) { sem_wait(&semaphore); // P(S) 연산 (진입) std::cout << "임계구역 실행 중...\n"; sem_post(&semaphore); // V(S) 연산 (해제) return nullptr; } int main() { sem_init(&semaphore, 0, 1); // 초기값 1 (binary semaphore) pthread_t t1, t2; pthread_create(&t1, nullptr, worker, nullptr); pthread_create(&t2, nullptr, worker, nullptr); pthread_join(t1, nullptr); pthread_join(t2, nullptr); sem_destroy(&semaphore); }  세마포어를 잘못 사용할 경우 발생할 위험성데드락(Deadlock)여러 프로세스가 세마포어를 무한정 대기하면 교착 상태 발생.기아 상태(Starvation)특정 프로세스가 세마포어를 계속 점유하면, 다른 프로세스는 계속 대기해야 함.우선순위 반전(Priority Inversion)낮은 우선순위 프로세스가 세마포어를 점유하면, 높은 우선순위 프로세스가 대기할 수 있음.모니터(Monitor)란?세마포어의 단점을 해결한 상호 배제(Mutual Exclusion) 매커니즘.프로그래머가 wait()과 signal()을 직접 사용하지 않고, 자동으로 동기화를 제공.Java에서 모니터 예제class SharedResource { synchronized void print() { System.out.println(Thread.currentThread().getName() + " 실행 중..."); } } class MyThread extends Thread { SharedResource sr; MyThread(SharedResource sr) { this.sr = sr; } public void run() { sr.print(); } } public class MonitorExample { public static void main(String[] args) { SharedResource sr = new SharedResource(); Thread t1 = new MyThread(sr); Thread t2 = new MyThread(sr); t1.start(); t2.start(); } }synchronized 키워드를 사용하면, 임계구역에 하나의 스레드만 진입할 수 있음.프로그래머가 직접 세마포어를 관리할 필요 없이 안전한 동기화를 제공.[섹션 05]데드락 데드락(Deadlock)이란?정의:데드락(교착 상태)은 두 개 이상의 프로세스가 서로 상대방의 작업이 끝나기를 기다리면서 무한히 멈춰 있는 상태를 의미예제:식사하는 철학자 문제다섯 명의 철학자가 둥근 테이블에서 식사를 하려면 양옆의 포크 두 개를 동시에 집어야 한다.모든 철학자가 왼쪽 포크를 집고 오른쪽 포크를 기다리면 교착 상태(Deadlock)가 발생하여 아무도 식사를 할 수 없게 된다. 교착 상태 (Deadlock)정의:여러 프로세스가 서로 다른 프로세스의 작업이 끝나기를 기다리다가 아무도 작업을 진행하지 못하는 상태교착 상태 발생 조건 (4가지 필요조건)데드락이 발생하려면 다음 4가지 조건이 동시에 만족해야 함이 중 하나라도 충족되지 않으면 데드락은 발생하지 않음!! 상호 배제 (Mutual Exclusion)한 번에 하나의 프로세스만 특정 자원을 사용할 수 있어야 함예: 한 개의 프린터를 여러 프로세스가 사용할 경우, 한 프로세스가 사용하는 동안 다른 프로세스는 대기해야 함.2. 점유와 대기 (Hold and Wait)프로세스가 이미 할당받은 자원을 보유한 채로 추가적인 자원을 기다려야 한다.예: 프로세스 A는 프린터를 가지고 있고, 프로세스 B가 사용하는 스캐너를 기다리는 상황비선점 (No Preemption)할당된 자원을 강제로 빼앗을 수 없음즉, 자원을 점유한 프로세스가 스스로 해제할 때까지 다른 프로세스는 기다려야 한다.순환 대기 (Circular Wait)프로세스들이 서로 원형(사이클) 형태로 자원을 점유하고 대기한다.예:A → B가 가진 자원을 기다림B → C가 가진 자원을 기다림C → A가 가진 자원을 기다림이처럼 순환 구조가 형성되면 데드락 발생! 데드락 해결 방법데드락을 해결하기 위해 예방, 회피, 검출 및 복구 방법이 존재 데드락 예방 (Prevention)데드락 필요조건 4가지 중 하나 이상을 제거하여 방지하는 방법방법:상호 배제 제거: 여러 프로세스가 동시에 자원을 공유하도록 설계점유와 대기 방지: 모든 자원을 한 번에 요청하도록 함비선점 가능하게 변경: 자원을 강제로 회수할 수 있도록 설정순환 대기 방지: 자원에 우선순위를 부여하여 순환이 생기지 않도록 설계  데드락 회피 (Avoidance)시스템이 미리 안전 상태(Safe State)를 유지하며 자원 할당을 조절하는 방법대표적인 알고리즘:은행원 알고리즘 (Banker’s Algorithm)프로세스가 최대 자원 요청량을 미리 선언해야 함시스템은 프로세스가 요청하는 자원을 할당했을 때 데드락이 발생하지 않는지 검토한 후 할당장점교착 상태 예방 가능안전 상태에서만 자원 할당 → 시스템 안정성 증가 단점프로세스가 최대 요구량을 미리 선언해야 함시스템이 항상 가능한 상태를 계산해야 하므로 오버헤드 발생 교착 상태 검출 및 복구이미 발생한 데드락을 감지하고 해결하는 방법방법:자원 할당 그래프(Resource Allocation Graph, RAG) 분석교착 상태 발생 시 체크포인트로 롤백교착 상태 프로세스 강제 종료 또는 자원 선점교착 상태 검출1) 가벼운 교착 상태 검출시스템이 주기적으로 자원 할당 그래프를 분석하여 교착 상태 가능성을 확인2) 체크포인트 롤백 (Checkpoint Rollback)마지막으로 저장한 체크포인트(Checkpoint)로 프로세스를 되돌려 교착 상태 해소[섹션 06]컴파일과 프로세스프로그램이 실행되는 과정 소스 코드 작성 (C, Java, Python 등)컴파일 (Compile)소스 코드를 기계어(바이너리 코드)로 변환링크 (Link)여러 개의 오브젝트 파일(.o, .obj)을 합쳐 실행 가능한 파일(.exe) 생성로드 (Load)실행 파일을 메모리에 로드실행 (Execute)CPU가 프로그램 명령어 실행[섹션 07] 메모리 메모리의 종류 메모리 주소1) 물리 주소 (Physical Address)실제 RAM의 주소CPU가 직접 접근하는 주소2) 논리 주소 (Logical Address)프로그램이 보는 가상의 주소OS가 논리 주소를 물리 주소로 변환재배치 레지스터 (Relocation Register)논리 주소를 물리 주소로 변환하는 데 사용되는 레지스터프로그램을 다른 메모리 위치로 이동해도 주소 수정 없이 실행 가능메모리 오버레이 (Memory Overlay)큰 프로그램을 작은 조각(Overlay)으로 분할하여 필요한 부분만 메모리에 적재하는 기법초창기 컴퓨터에서 메모리가 부족할 때 사용메모리 할당 방식 (2가지)1. 고정 분할 방식 (Fixed Partitioning)메모리를 고정된 크기의 블록(파티션)으로 나누고, 프로세스를 할당하는 방식각 파티션 크기는 시스템 부팅 시 미리 정해짐단순한 구조이지만 메모리 낭비(내부 단편화)가 발생할 수 있음특징프로세스가 할당된 크기보다 작으면 남은 공간이 낭비됨 (내부 단편화 발생)파티션 크기를 변경할 수 없음작은 프로세스가 들어가야 할 공간에 큰 프로세스가 오면 메모리 낭비가 발생관리가 쉬운 대신 유연성이 부족장점관리가 단순함 (구현 쉬움)메모리 할당/해제 속도가 빠름CPU 스케줄링이 단순해짐단점내부 단편화(Internal Fragmentation) 발생 → 프로세스보다 큰 파티션을 할당받으면 남는 공간이 낭비됨파티션 크기를 미리 설정해야 함 → 크기 조정 불가능큰 프로그램을 실행하기 어려움 (적절한 크기의 파티션이 없으면 실행 불가)예시2.가변 분할 방식 (Variable Partitioning)프로세스가 요청하는 크기만큼 메모리를 할당하는 방식동적 메모리 할당 방식 → 프로세스가 들어올 때마다 필요한 만큼만 메모리를 할당메모리를 효율적으로 사용할 수 있지만, 외부 단편화 문제 발생특징메모리 크기에 맞게 프로세스를 할당하므로 내부 단편화가 없음하지만 메모리가 여러 개의 작은 조각으로 나뉘어 외부 단편화 발생메모리를 동적으로 관리할 수 있어 유연성이 높음장점내부 단편화 없음 → 프로세스 크기에 맞춰 메모리를 할당큰 프로그램도 실행 가능 → 고정된 크기 제한이 없음메모리 활용률이 높음단점외부 단편화(External Fragmentation) 발생 → 메모리 사이에 작은 빈 공간이 많아져 사용 불가메모리 관리가 복잡 → 동적 할당을 위해 추가적인 관리 필요메모리 검색 시간 증가 → 적절한 크기의 공간을 찾기 위해 시간이 오래 걸릴 수 있음 예시메모리 단편화1) 외부 단편화 (External Fragmentation)메모리가 남아 있지만, 연속된 공간이 부족하여 할당 불가능한 상태해결 방법: 메모리 압축(Compaction) 또는 페이징(Paging) 기법 사용2) 내부 단편화 (Internal Fragmentation)고정 분할 방식에서 할당된 공간보다 작은 프로그램이 로드될 때 남는 공간해결 방법: 가변 분할 방식 사용   '그림으로 쉽게 배우는 자료구조와 알고리즘(기본편)' 2주차[섹션 03]알고리즘  재귀함수 (Recursive Function)란?자기 자신을 호출하는 함수를 재귀 함수라고 함문제를 작은 부분으로 나누어 해결하는 방식으로, 주로 반복적인 구조의 문제를 해결할 때 사용재귀 함수의 탈출 조건 (Base Case)이란?재귀 호출이 무한히 반복되지 않도록 종료되는 조건을 기저 조건(Base Case) 이라고 함만약 기저 조건이 없으면 무한 루프에 빠져 프로그램이 멈추지 않는 문제가 발생할 수 있음재귀함수 예시: 팩토리얼 계산 c++#include <iostream> using namespace std; // 팩토리얼 함수 (재귀) int factorial(int n) { if (n == 0) return 1; // 기저 조건 (탈출 조건) return n * factorial(n - 1); // 재귀 호출 } int main() { cout << factorial(5); // 5! = 5 * 4 * 3 * 2 * 1 = 120 return 0; }factorial(0)의 경우 1을 반환하면서 재귀 호출이 종료 재귀적으로 생각하는 방법?작은 문제로 나눈다 → 현재 문제를 더 작은 문제로 분할2. 탈출 조건을 찾는다 → 가장 작은 입력에 대한 결과를 명확하게 정의3. 점화식(재귀 관계식)을 찾는다 → 현재 상태와 작은 문제 사이의 관계 정의4. 재귀 호출을 구현한다 → 주어진 입력에서 자기 자신을 호출하는 구조예제: 피보나치 수열int fibonacci(int n) { if (n <= 1) return n; // 기저 조건 return fibonacci(n - 1) + fibonacci(n - 2); // 재귀 호출 }fibonacci(0) = 0, fibonacci(1) = 1 → 기저 조건을 만족하면 종료  하노이 탑 문제 (Hanoi Tower)세 개의 기둥과 여러 개의 원반이 있음한 번에 하나의 원반만 이동 가능작은 원반이 큰 원반 위에 올라가면 안 됨재귀적 풀이1. 가장 큰 원반을 제외한 나머지를 보조 기둥으로 이동2. 가장 큰 원반을 목표 기둥으로 이동3. 보조 기둥의 원반들을 다시 목표 기둥으로 이동#include <iostream> using namespace std; void hanoi(int n, char from, char to, char aux) { if (n == 1) { cout << "Move disk 1 from " << from << " to " << to << endl; return; } hanoi(n - 1, from, aux, to); // n-1개를 보조 기둥으로 이동 cout << "Move disk " << n << " from " << from << " to " << to << endl; hanoi(n - 1, aux, to, from); // 보조 기둥에서 목표 기둥으로 이동 } int main() { hanoi(3, 'A', 'C', 'B'); return 0; }정렬 알고리즘버블 정렬 (Bubble Sort)인접한 두 개의 값을 비교하여 정렬가장 큰 값이 점점 오른쪽으로 이동하는 방식시간 복잡도최선(O(n)) → 이미 정렬된 경우최악(O(n²)) → 완전히 뒤집힌 경우  장점 & 단점구현이 간단함안정 정렬(Stable Sort)시간 복잡도가 높아 비효율적C++ 코드#include <iostream> using namespace std; void bubbleSort(int arr[], int n) { for (int i = 0; i < n - 1; i++) { for (int j = 0; j < n - i - 1; j++) { if (arr[j] > arr[j + 1]) { swap(arr[j], arr[j + 1]); } } } } int main() { int arr[] = {5, 2, 9, 1, 5, 6}; int n = sizeof(arr) / sizeof(arr[0]); bubbleSort(arr, n); for (int i : arr) cout << i << " "; return 0; }선택 정렬 (Selection Sort)가장 작은 값을 찾아서 앞쪽에 배치하는 방식정렬된 부분과 정렬되지 않은 부분으로 나눠 진행시간 복잡도O(n²) (최선, 최악 동일)  장점 & 단점비교 횟수가 일정하여 안정적추가적인 메모리 사용이 적음시간 복잡도가 높아 비효율적안정 정렬이 아님 (같은 값의 순서가 바뀔 수 있음) C++ 코드#include <iostream> using namespace std; void selectionSort(int arr[], int n) { for (int i = 0; i < n - 1; i++) { int minIdx = i; for (int j = i + 1; j < n; j++) { if (arr[j] < arr[minIdx]) { minIdx = j; } } swap(arr[i], arr[minIdx]); } } int main() { int arr[] = {5, 2, 9, 1, 5, 6}; int n = sizeof(arr) / sizeof(arr[0]); selectionSort(arr, n); for (int i : arr) cout << i << " "; return 0; } 삽입 정렬 (Insertion Sort)현재 원소를 정렬된 부분에 적절히 삽입하는 방식버블, 선택 정렬보다 효율적작은 데이터에서 성능이 좋음시간 복잡도최선(O(n)) → 거의 정렬된 경우최악(O(n²)) → 역순 정렬된 경우  장점 & 단점 정렬된 부분을 활용하여 효율적으로 정렬 가능비교적 빠른 알고리즘 (특히 데이터가 거의 정렬된 경우)큰 데이터에서는 비효율적C++ 코드#include <iostream> using namespace std; void insertionSort(int arr[], int n) { for (int i = 1; i < n; i++) { int key = arr[i]; int j = i - 1; while (j >= 0 && arr[j] > key) { arr[j + 1] = arr[j]; j--; } arr[j + 1] = key; } } int main() { int arr[] = {5, 2, 9, 1, 5, 6}; int n = sizeof(arr) / sizeof(arr[0]); insertionSort(arr, n); for (int i : arr) cout << i << " "; return 0; } 정렬 알고리즘 비교배우고 느낀점한 주가 지났다고 벌써 운영체제 앞부분의 기억이 희미해져서 강의를 보면서 다시 앞부분 내용을 일부 확인하는 과정이 있었습니다.. 복습을 다시 해야겠다는 생각을 하게 됐습니다.CS를 알아야 컴퓨터의 모든 실행 과정(메모리 할당 및 컴파일 과정)을 알 수 있다는 것을 다시 한번 느끼게 되었습니다.처음 접한 단어들이 많아서 다시 한번 학습해야 할 것 같습니다. 어려웠던 점수학 지식이 부족해서 시간복잡도를 이해하기 힘들었습니다. (어떤게 빠르고 어떤게 느린건지)전 주에 들었던 강의 내용을 일부만 기억해서 이번 과정을 이해하는데 힘들었습니다. 앞으로 개선할 점운영체제 초반부분부터 다시 복습하기..코드 구현 시 혼자 생각하면서, 구현 절차를 먼저 생각하는 데에 시간을 많이 소모한 후 구현하기=> 먼저 구현하면서 구현 과정 중 생각하기 X

찬우 이

인프런 워밍업 클럽 3기 CS - 2주차 발자국

2주차 학습 내용 - 발자국자료구조 & 알고리즘재귀함수function sum(n) { if (n === 1) return 1; // ✅ 기본 조건(Base Case): n이 1이면 재귀 종료 return n + sum(n - 1); // 🔁 재귀 호출(Recursive Case): sum(n-1) 호출 } console.log(sum(5)); // 5 + 4 + 3 + 2 + 1 = 15함수 내부에서 자기 자신을 다시 호출하는 구조를 가진 함수기본조건재귀 함수가 계속 반복되지 않도록 종료 조건(기저 조건)을 설정해야 함.없으면 무한 루프가 발생하여 Stack Overflow(스택 오버플로우) 오류가 발생함.재귀 호출재귀 호출을 통해 문제를 점점 더 단순하게 만들어 Base Case에 도달하도록 함. 하노이 탑(재귀)function hanoi(count, from, to, temp) { if (count === 0) return; hanoi(count - 1, from, temp, to); console.log(`원반${count}를 ${from}에서 ${to}로 이동했습니다.`); hanoi(count - 1, temp, to, from); } hanoi(3, "A", "C", "B"); 재귀문제의 기본으로 알려진 하노이탑 문제의 원리큰 원반을 옮기기 전에, 그 위에 있는 원반들을 다른 기둥으로 옮긴다.가장 아래에 있는 큰 원반을 목표 기둥으로 이동한다.다른 기둥에 옮겨둔 원반들을 다시 목표 기둥으로 옮긴다.hanoi 함수의 매개변수는 (원반개수, 시작위치, 도착위치, 거치는위치)4개로 구성된다.   버블정렬function bubbleSort(arr) { let n = arr.length; for (let i = 0; i < n - 1; i++) { // 전체 반복 횟수 for (let j = 0; j < n - 1 - i; j++) { // 점점 줄어드는 비교 범위 if (arr[j] > arr[j + 1]) { // 오름차순 정렬 [arr[j], arr[j + 1]] = [arr[j + 1], arr[j]]; // Swap (교환) } } } return arr; } let arr = [5, 3, 8, 4, 2]; console.log(bubbleSort(arr)); // [2, 3, 4, 5, 8] 배열을 반복하면서 인접한 두 요소를 비교하고 조건에 따라 서로의 위치를 바꿈구현하기는 쉽지만 성능면(O(n²))에서는 그렇게 좋지 않음.  선택정렬function selectionSort(arr) { let n = arr.length; for (let i = 0; i < n - 1; i++) { let minIndex = i; // 현재 정렬된 부분 이후에서 가장 작은 값의 인덱스 저장 for (let j = i + 1; j < n; j++) { // i 이후 요소들과 비교 if (arr[j] < arr[minIndex]) { minIndex = j; // 더 작은 값이 발견되면 minIndex 갱신 } } // 최소값을 현재 위치(i)와 교환 (swap) if (minIndex !== i) { [arr[i], arr[minIndex]] = [arr[minIndex], arr[i]]; } } return arr; } let arr = [5, 3, 8, 4, 2]; console.log(selectionSort(arr)); // [2, 3, 4, 5, 8] 배열영역의 첫 번째 원소를 시작으로 마지막 원소까지 비교 후 가장 작은 값을 첫 번째 원소로 가져온다.버블정렬과 같은 경우로 구현하기는 쉽지만 성능면(O(n²))에서는 그렇게 좋지 않음. 운영체제 프로세스 간 통신한 컴퓨터내에 있는 다른 프로세스와 통신하는 방법 ex) 파일, 파이프파일: 프로세스 간 데이터를 저장하고 읽을 수 있음.파이프: 한 프로세스의 출력이 다른 프로세스의 입력이 되는 방식.네트워크로 연결된 다른 컴퓨터에 있는 프로세스와 통신을 하는 방법 ex) 소켓통신, RPC소켓통신: 네트워크를 통해 데이터를 주고받는 방식RPC: 원격 프로시저 호출을 이용하여 다른 컴퓨터에서 실행되는 함수 호출.쓰레드 간 통신같은 프로세스 내에서 실행되는 여러 개의 쓰레드 간의 통신 방법쓰레드는 코드, 데이터, 힙을 공유하며, 각자의 스택만 별도로 가짐.전역 변수(Global Variable)나 힙(Heap)을 이용하면 쓰레드 간 데이터 공유 가능. 공유자원프로세스나 쓰레드 간의 통신에서 공동으로 사용하는 변수나 파일을 의미한다.임계구역여러 프로세스가 동시에 접근하면 안 되는 공유 자원의 영역세마포어공유자원을 함께 쓰는 프로세스 간의 충돌을 막기 위해 프로세스가 사용하는 동안 다른 프로세스는 동작하지 않고 기다리는 것모니터세마포어의 단점을 개선한 방법운영체제(OS) 차원이 아니라, 프로그래밍 언어에서 제공하는 동기화 방법내부적으로 뮤텍스(Mutex)와 조건 변수(Condition Variable)를 사용하여 동기화 수행여기부터교착상태(데드락)프로세스들이 서로가 가진 자원을 기다리면서 아무것도 실행되지 않는 상태각 프로세스가 다른 프로세스의 작업이 끝나기를 기다리지만, 아무도 자원을 해제하지 않음교착상태가 발생하려면 다음 4가지 조건이 모두 충족되어야 한다.상호배제: 자원은 한 번에 하나의 프로세스만 사용할 수 있어야 한다.비선점: 점유한 자원을 강제로 빼앗을 수 없다.점유와 대기: 이미 자원을 점유한 상태에서 추가적인 자원을 기다려야 한다.원형 대기: 프로세스들이 서로 다음 프로세스의 자원을 기다리는 원형 구조가 형성되어야 한다.하지만 필요조건을 지켜도 교착상태는 발생한다는 것을 깨달았고, 교착상태를 예방하기보단 교착상태가 발생했을때 해결하는 방법을 찾으려고 했다.교착상태 해결방안교착상태 회피프로세스들에게 자원을 할당할 때 어느정도 자원을 제공해야 교착상태가 발생하는지 파악해서 교착상태가 발생하지 않는 선에서 할당해주는 것전체 자원의 수와 할당된 자원의 수를 비교해서 안정상태와 불안정 상태로 나눔시스템의 총 자원과 각 프로세스간의 최대 요구자원을 계산해서 여유분의 자원을 남기고 프로세스에게 제공해야 안정상태를 유지할 수 있다.교착상태 검출가벼운 교착 상태 검출: 타이머를 이용해 프로세스가 일정시간 동안 작업을 진행하지 않으면 교착상태가 일어났다고 생각하고 해결함(해결 방법은 주기적으로 상황을 업데이트해서 만약 교착이라고 느끼면 롤백해서 이전으로 되돌아감)무거운 교착 상태 검출: 자원 할당 그래프를 이용하며, 교착 상태를 발견하면(발견은 자원이 순환하면 교착상태임) 해결함.해결방법은 교착상태를 인지하면 교착을 일으킨 프로세스를 강제종료하고 다시 실행할때 이전 업데이트로 롤백함.컴파일 언어소스 코드 전체를 한 번에 기계어(0과 1)로 변환한 후 실행하는 언어속도가 빠름.언어: C, C++, C# 등컴파일에서 실행파일로 변환 과정test.c -> 전처리기 -> test.i -> 컴파일러 -> test.s -> 어셈블리 -> test.o -> 링커 -> test.exe1⃣ test.c → 전처리기(Preprocessor) → test.i (주석 제거, 매크로 처리 등)2⃣ test.i → 컴파일러(Compiler) → test.s (어셈블리 코드 생성)3⃣ test.s → 어셈블러(Assembler) → test.o (목적 파일 생성)4⃣ test.o → 링커(Linker) → test.exe (최종 실행 파일 생성) 인터프리터 언어코드를 한 줄씩 읽고 실행하는 방식의 언어컴파일 과정 없이 즉시 실행되지만, 실행 속도는 컴파일 언어보다 느림언어: JS, Python, Ruby 등 메모리 종류 레지스터CPU 내부에 존재하는 가장 빠른 기억장소매우 빠른 연산을 위해 사용되며, CPU가 직접 접근할 수 있음휘발성(Volatile) 메모리 → 전원이 꺼지면 데이터가 사라짐 캐시메인 메모리(RAM)와 CPU(레지스터) 사이에 위치하는 고속 메모리CPU가 자주 사용하는 데이터를 미리 저장하여 접근 속도를 높임 메인메모리(RAM)운영체제(OS)와 실행 중인 프로그램이 올라가는 공간휘발성 메모리가격이 비싸기 때문에, 데이터 저장보다는 실행 중인 프로그램을 로드하는 용도로 사용HDD(하드디스크)나 SSD보다 훨씬 빠르지만, 레지스터나 캐시보다는 느림 보조저장장치(HDD,SSD)가격이 저렴하며, 데이터를 영구적으로 저장하는 용도로 사용됨비휘발성(Non-Volatile) 메모리 → 전원이 꺼져도 데이터가 유지됨 메모리 할당 방식 메모리 오버레이프로그램이 메모리보다 클 경우, 실행에 필요한 부분만 메모리에 로드하는 방식나머지 코드는 하드디스크에 저장되며, 필요할 때만 메모리에 불러옴가변 분할 방식프로세스 크기에 맞춰 메모리를 동적으로 분할하는 방식외부단편화 발생: 여러 개의 작은 빈 공간이 생겨 새로운 프로세스를 할당하기 어려운 문제고정 분할 방식프로세스 크기와 상관없이 미리 정해진 크기로 메모리를 나누는 방식장점: 구현이 간단하고 오버헤드가 적음단점: 작은 프로세스는 큰 영역에 할당되서 공간이 낭비되는 내부단편화 발생버디 세스템가변 분할 방식과 고정 분할 방식을 혼합하여 단점을 최소화한 메모리 할당 방식메모리를 2의 승수 크기로 분할하여 할당2주차 회고재귀가 너무 어렵다.. 정렬은 그래도 예전에 공부해본적이 있어서 한두번 더 보니까 이해가 되는데 재귀는 봐도봐도 이해가 어려움..특히 하노이ㅋㅋㅋㅋ 새로운 벽이였다. 그래도 자주 보다보니 적응이 되는것 같기도 하고 아닌거 같기도하고,,,그렇다 보니 미션 3번째 문제는 도무지 이해가 쉽지 않았다. 그래서 GPT의 도움과 함께 계속 이해해려하고 있고 지금도 하고있다,,ㅎ그리고 2주차때는 중간점검을 통해 다같이 구글밋을 했다. 주 내용은 운영체제같은 CS지식이 있으면 다른 프레임워크나 컴퓨터 쪽의 지식을 쌓고 배울때 지식이 없는사람에 비해 더 빠르게 습득할 수 있고, 흡수하는게 빠르다고 했다. 벌써 다음주가 3주차라서 CS는 마지막 주 인데 마무리 잘해서 수료하고, 수료 이후에도 강의 반복해서 듣고 자료구조 & 알고리즘은 심화버전이 있어서 그걸 들어야 할 것 같다.

알고리즘 · 자료구조자료구조알고리즘운영체제인프런워밍업클럽CS

당황한 수달

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

PM/PO로 성장하기 위해, 고객과 데이터에 대한 전문성을 쌓는 것이 얼마나 중요한지 배운 한 주였습니다. 이번 주 강의(시작하는 PM/PO들에게 알려주고 싶은, 프로덕트의 모든 것)는 크게 두 가지 섹션(고객/데이터에 대한 전문성)으로 구성되었습니다.  2주 차 동안 무엇을 배웠는가?1. 고객 전문가가 되기 위한 접근법PM은 단순한 기획자가 아니라 고객의 문제를 해결하는 사람입니다.이를 위해 고객을 깊이 이해해야 하며, 그 방법 중 하나가 심층 인터뷰와 사용성 인터뷰입니다.강의에서는 단순히 "고객을 만나면 좋다"가 아니라,리서치를 왜 하는지어떤 목적을 가지고 진행해야 하는지각 인터뷰 방법을 어떻게 실행하는지그리고 구체적인 사례를 바탕으로 다뤄졌습니다.특히 튜터님이 직접 진행하셨던 실제 리서치 사례(모집 방법, 사전 질문을 통한 필터링 기법 등)를 공유해 주셔서, 단순한 이론이 아니라 실무에서 어떻게 활용할 수 있을지 감을 잡을 수 있던 소중한 시간이었습니다. 2. 목적이 있어야 의미가 있는 데이터PM이 데이터를 활용할 때도 "어떤 데이터를 모을 것인가"가 아니라 "이 데이터를 통해 무엇을 검증할 것인가"가 중요하다는 것을 다시한번 느꼈습니다. 단순히 데이터를 축적하는 것이 아니라, 이를 통해 의사결정을 내릴 수 있어야 한다는 점을 강조하셨습니다.2주 차 회고이번 주 강의를 통해 가장 크게 배운 것은 "무엇을 하든 목적을 명확히 해야 한다"는 점입니다.특히 저는 과거에 사용성 인터뷰와 심층 인터뷰를 진행한 경험이 있지만, 지금 돌아보면 아쉬운 점이 많았습니다. 당시에는 일단 하면 답이 나올 것이라는 막연한 기대만 있었고, 명확한 검증 목표 없이 진행했기 때문입니다.이번 강의를 통해 내가 했던 실수를 인지하고, 어떻게 개선할 수 있을지 고민할 기회가 되었습니다. 또한, 실시간 온라인 라이브에서 튜터님께 직접 질문하며 부족했던 부분을 보완할 수 있었던 것이 특히 좋았습니다. 다음 주 학습 계획은?다음 주는 지표를 깊이 탐구하는 강의로 구성되어 있습니다. 일정이 다소 빠듯하긴 하지만, 강의 목차를 보니 익숙한 개념들이 포함되어 있어 복습하는 마인드로 접근하려 합니다. 다만, 단순한 복습이 아니라 내가 미처 인지하지 못했던 핵심 포인트를 짚어내는 것에 집중하려고 합니다. 기존에 알고 있던 개념이라도 더 깊이 이해하고 실무에서 활용할 수 있도록, 중요한 부분을 꼼꼼히 파악하며 학습할 계획입니다.

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

강동훈

[인프런 워밍업 클럽 3기 - CS] - 2주차 미션 (자료구조와 알고리즘)

재귀함수에서 기저조건을 만들지 않거나 잘못 설정했을 때 어떤 문제가 발생할 수 있나요?재귀함수는 함수 내부에서 자기 자신을 다시 호출하여 작업을 수행하는 함수를 의미한다. 즉, 자기 자신을 무한대로 호출하여 작업하기 때문에 함수 종료 조건인 기저조건을 설정하지 않는다면, 해당 함수가 실행됨에 따라 무한대로 콜스택에 메모리가 얹히게 되고 스택 오버플로우가 발생하여 프로그램이 강제 종료된다.// 기저 조건 없는 경우 function factorial(n){ return n * factorial(n - 1) } // RangeError : Maxmum call stack size exceeded // 기저 조건 설정 function factorial(n) { if (n == 0) return 1; return n * factorial(n - 1); }0부터 입력 n까지 홀수의 합을 더하는 재귀 함수를 만들어보세요.하위조건 : n - 1이 홀수인지 확인하고 홀수일 경우 n을 더하고 짝수일 경우 0을 더함기저조건: n이 0 이하일 경우 0을 반환하고 함수 종료function sumOdd(n){ // 재귀 로직 if (n <= 0) return 0; let oddNum = n % 2 === 0 ? 0 : n; return oddNum + sumOdd(n - 1); } console.log(sumOdd(10)) // 25다음 코드는 매개변수로 주어진 파일 경로(.는 현재 디렉토리)에 있는 하위 모든 파일과 디렉토리를 출력하는 코드입니다. 다음 코드를 재귀 함수를 이용하는 코드로 변경해보세요.const fs = require('fs'); // 파일을 이용하는 모듈 const path = require('path'); // 폴더와 파일의 경로를 지정해주는 모듈 function traverseDirectoryRecursive(directory) { const files = fs.readdirSync(directory); // 1. 인자로 받은 폴더 내부 파일들 추출 for (const file of files) { const filePath = path.join(directory, file); // 2. 파일 경로 합치기 const fileStatus = fs.statSync(filePath); // 2. 파일 정보 얻기 if (fileStatus.isDirectory()) { // 3-1. 폴더일 경우 재귀 console.log('디렉토리:', filePath); traverseDirectoryRecursive(filePath); } else { // 3-2. 파일일 경우 출력 console.log('파일:', filePath); } } } traverseDirectoryRecursive('.'); // 현재 경로의 모든 하위 경로의 파일, 디렉토리 출력하위 조건:인자로 받은 Directory의 파일과 폴더를 읽어온다파일 경로를 합치고 파일 정보를 얻어온다폴더일 경우, 재귀함수를 통해 내부 폴더의 파일과 폴더를 읽는다파일일 경우, 파일을 출력한다.기저조건:현재 폴더 내부 모든 파일 수만큼 반복📔 회고알고리즘 문제가 아닌 실전에서 사용할 수 있는 재귀 함수로 응용을 해보니 생각보다 하위조건을 파악하고 기저조건을 설정하는 것이 쉽지 않다는 것을 깨달았다. 처음에는 계속해서 코드를 읽어보면서 익숙하지 않은 fs모듈에 대해서 먼저 파악해보고, 제공되는 메서드들을 익혀보았다. 그렇게 코드의 흐름을 익혀가면서 반복되는 부분을 구분하였고, 재귀적으로 해결할 수 있는 부분은 while 문이라는 것을 파악했다. 기존에 스택을 통해서 파일들을 가져오고 데이터를 쌓아오면서 while 문을 통해 스택에 있는 데이터를 다시 출력하는 코드였다는 것을 파악하였고, 이를 재귀적으로 변경하기 위해서는 스택 자료구조를 사용하지 않고 하나의 함수에 하나의 폴더를 읽어오고 재귀적으로 함수를 다시 호출하면서 폴더 내부의 파일을 찾아가는 형식으로 수정할 수 있다는 것을 파악했다. 그렇게 하위조건을 설정하였고 기저조건을 만들어서 성공적으로 재귀함수로 코드를 수정할 수 있었다.이렇게 알고리즘을 응용하여 실전에서 사용할 수 있다는 것을 크게 깨달았고, 앞으로 알고리즘을 배울 때도 실전에서도 사용될 수 있는 다양한 사례를 함께 찾아보면서 공부하면 더 알고리즘 개념을 탄탄히 가져갈 수 있을 것 같다.

알고리즘 · 자료구조자료구조인프런워밍업

동동

[인프런 워밍업 클럽_3기 CS] 두번째 발자국 🐾🐾

📌 이 글은 워밍업 클럽 3기 감자님의 CS 강의인 ‘그림으로 쉽게 배우는 자료구조와 알고리즘’과 ‘그림으로 쉽게 배우는 운영체제’를 학습하며 정리한 내용을 담고있습니다. 🙇‍♂1⃣ 자료구조와 알고리즘📌 1. 재귀 (Recursion)재귀 개념재귀란, 자기 자신을 호출하는 방식으로 문제를 해결하는 기법콜 스택 (Call Stack)을 사용하여 함수 호출을 관리대표적인 예 : 팩토리얼 계산, 하노이 탑 문제 하노이 탑 (Tower of Hanoi)원반을 규칙에 맞게 기둥 사이로 이동시키는 문제재귀 호출을 활용하여 해결 가능const hanoi = (count, from, to, temp) => { if (count == 0) return; hanoi(count - 1, from, temp, to); console.log(`원반 ${count}를 ${from}에서 ${to}로 이동`); hanoi(count - 1, temp, to, from); }; hanoi(3, 'A', 'C', 'B');재귀적으로 생각하는 두 가지 패턴단순히 반복 실행하는 방식하위 문제의 결과를 기반으로 현재 문제를 계산하는 방식 (하향식 계산) 📌 2. 정렬 알고리즘버블 정렬 (Bubble Sort)인접한 두 데이터를 비교하며 정렬하는 방식시간 복잡도 : O(n²) (비효율적)장점 : 이해하기 쉽고 구현이 간단함단점 : 성능이 좋지 않음for (let i = 0; i < arr.length - 1; i++) { for (let j = 0; j < arr.length - i - 1; j++) { if (arr[j] > arr[j + 1]) { let temp = arr[j]; arr[j] = arr[j + 1]; arr[j + 1] = temp; } } } 선택 정렬 (Selection Sort)가장 작은 값을 선택하여 앞으로 이동시간 복잡도 : O(n²)장점 : 이해하기 쉽고 구현이 간단함단점 : 성능이 좋지 않음for (let i = 0; i < arr.length - 1; i++) { let minValueIndex = i; for (let j = i + 1; j < arr.length; j++) { if (arr[j] < arr[minValueIndex]) { minValueIndex = j; } } let temp = arr[i]; arr[i] = arr[minValueIndex]; arr[minValueIndex] = temp; } 2⃣ 운영체제📌 1. 운영체제 개요운영체제의 역할프로세스 관리 : CPU 스케줄링, 멀티태스킹메모리 관리 : 프로세스 메모리 할당파일 및 입출력 관리 : 파일 시스템, 장치 드라이버 제어컴파일 과정test.c -> 전처리기 -> test.i -> 컴파일러 -> test.s -> 어셈블러 -> test.o -> 링커 -> test.exe전처리기 : #include, #define 등을 처리컴파일러 : C 코드를 어셈블리 코드로 변환어셈블러 : 어셈블리 코드를 기계어로 변환링커 : 실행 가능한 바이너리 파일 생성 📌 2. 프로세스 스케줄링FIFO (First In First Out)도착 순서대로 CPU를 할당장점 : 구현이 간단하고 공정함단점 : 실행 시간이 긴 프로세스가 먼저 도착하면 대기 시간이 증가 SJF (Shortest Job First)실행 시간이 짧은 프로세스를 우선 실행단점 : 프로세스의 실행 시간을 미리 예측하기 어려움 RR (Round Robin)타임 슬라이스(Time Slice)를 사용하여 프로세스를 순환 실행타임 슬라이스가 너무 작으면 문맥 전환 비용이 증가하여 비효율적 MLFQ (Multi-Level Feedback Queue)CPU Bound Process와 I/O Bound Process를 구분하여 처리CPU Bound Process : CPU 사용 시간이 길면 낮은 우선순위 큐로 이동I/O Bound Process : CPU를 짧게 사용하며 높은 우선순위 큐 유지 📌 3. 프로세스 간 통신공유 자원과 임계 구역공유 자원 : 여러 프로세스가 공동으로 사용하는 변수, 메모리, 파일임계 구역 (Critical Section) : 한 번에 하나의 프로세스만 접근해야 하는 구역 임계 구역 문제 해결을 위한 3가지 조건하나의 프로세스만 접근 가능여러 요청이 있을 경우 순차적으로 접근 허용임계 구역에 들어간 프로세스는 빠르게 종료해야 함 세마포어 (Semaphore)동기화 기법으로 공유 자원에 대한 접근을 제한 모니터 (Monitor)세마포어의 단점을 보완한 기법프로그래밍 언어 수준에서 지원 (synchronized 키워드 사용) 📌 4. 데드락 (교착 상태)여러 프로세스가 서로 자원을 기다리며 작업이 멈추는 상태 데드락 발생 조건 (4가지)상호 배제 (Mutual Exclusion) : 한 번에 하나의 프로세스만 자원 사용 가능비선점 (No Preemption) : 자원을 강제로 빼앗을 수 없음점유와 대기 (Hold and Wait) : 자원을 점유한 상태에서 추가 자원을 기다림원형 대기 (Circular Wait) : 프로세스들이 서로 자원을 기다리며 원형 구조 형성 데드락 해결 방법교착 상태 회피 (Deadlock Avoidance) 자원 할당 시 교착 상태 발생 여부를 예측은행원 알고리즘 (Banker’s Algorithm) 사용하여 안전 상태 유지교착 상태 검출 및 해결가벼운 교착 상태 검출 : 일정 시간 동안 프로세스가 멈춰 있으면 강제 종료무거운 교착 상태 검출 : 운영체제가 직접 프로세스의 자원 사용을 모니터링 후 해결 📌 5. 메모리 관리메모리 종류레지스터 : CPU 내부의 가장 빠른 기억 장치 (휘발성)캐시 메모리 : CPU가 미리 가져온 데이터를 저장하는 고속 메모리메인 메모리 (RAM) : 실제 운영체제와 프로세스가 올라가는 공간 (휘발성)보조 저장 장치 (HDD, SSD) : 비휘발성 메모리 메모리 할당 방식가변 분할 방식 (Segmentation) : 프로세스 크기에 맞게 메모리를 할당고정 분할 방식 (Paging) : 프로세스 크기와 관계없이 일정한 크기로 할당 버디 시스템 (Buddy System)2의 승수 단위로 메모리를 분할하여 할당내부 단편화 최소화 & 프로세스 크기에 따라 유동적 할당 가능 3⃣ 회고첫 주차에 듣지 못했던 강의를 들으면서 다시 정상적으로 강의를 듣기 시작했다. 🙇‍♂ 재귀함수 강의 중 하노이 탑 내용은 혼자서 시도를 해보면 절대 구현을 못해낼 거 같다... 이 부분을 다시 공부해야할 것 같습니다 ㅠㅠ.. 이번 2주차 강의를 들으면서 자료구조와 알고리즘은 되게 묵직한 그런 느낌이고, 운영체제는 뭔가 이 컴퓨터란 존재에 대해서 다시 생각해보는 강의였던 것 같습니다..! 🐾🐾

인프런워밍업클럽CS

lkwo

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

2주차를 마무리하며...2주 차에는 예제 코드의 리팩토링을 직접 해보는 과제와 중간 점검이 있었습니다.강의를 보며 따라 할 때는 괜찮았지만, 막상 코드에서 리팩토링을 하려니 무엇부터 시작해야 할지 막막하더군요.리팩토링은 강의만 본다고 해결되는 영역이 아니라는 것을 느꼈고, 체화되기까지 시간이 필요하겠다고 생각한 한 주였습니다.강의내용 정리좋은 주석이란?주석이 많다는 것은 코드에 적절한 추상화를 적용하지 못해, 주석으로 코드를 설명하려는 것은 아닌지 의심해봐야 합니다.좋은 주석이란 코드로 전달할 수 없는 정보를 담는 것입니다.예를 들어, 의사 결정의 히스토리 같은 내용을 기록하는 것이 좋은 주석입니다.또한, 주석도 코드와 마찬가지로 버전 관리가 필요합니다. 관련된 의사 결정이 변경되었다면, 주석도 잊지 말고 함께 수정해야 합니다. 변수와 메서드 나열변수는 사용하는 위치와 최대한 가까이 두어 뇌 메모리 부담을 줄이는 것이 좋습니다.메서드와 변수의 접근자 종류에 따라 위치를 정리할 수 있습니다.메서드는 접근자 종류뿐만 아니라, 중요도와 로직의 종류를 기준으로 배치하면 더 정리된 코드를 만들 수 있습니다. 패키지 나누기패키지는 단순한 디렉터리가 아니라, 문맥적인 정보를 제공하는 역할을 합니다.적절하게 패키지를 나누면 코드의 가독성과 유지보수성이 향상됩니다.  IDE 활용하기정렬 단축키를 활용하여 코드 스타일을 통일합니다.Linting & 스타일 도구를 사용하여 코드 품질을 높일 수 있습니다.SonarLintEditorConfig리팩토링 직접 해보기리팩토링리팩토링에는 합리적인 이유가 필요하다리팩토링을 할 때는 단순히 코드 줄 수를 줄이는 것이 목표가 아니라, 객체의 역할과 책임을 올바르게 분배하고, 적절한 추상화를 적용하는지가 중요합니다. 이를 위해 리팩토링 과정에서 내가 합리적으로 수정하고 있는지 계속 의심하며 진행했습니다. (제가 정말 합리적으로 했는지는 모르겠습니다만...) 리팩토링 순서 정하기처음 코드를 봤을 때, 어디서부터 손을 대야 할지 막막함을 느꼈습니다. 그래서 나름의 리팩토링 순서를 정하고 진행해보았습니다.내가 읽기 좋은 코드로 변경하기공백 추가하기 (가독성 개선)함수 분리하여 추상화하기테스트하기 쉬운 구조로 변경하기역할과 책임을 고려하여 인터페이스와 객체로 분리하기 가독성 개선하기패키지 분리하기   리팩토링에는 정답이 없다리팩토링은 정해진 답이 없다는 점이 가장 어렵게 느껴지는 부분인 것 같습니다.  중간점검Day 4 미션 피드백아래는 제가 미션으로 제출한 코드입니다.이번 점검에 피드백으로 얻은 새로운 인사이트를 나열해봅니다.반환 타입이 boolean인 경우, 예외를 발생시키기 전에 해당 메서드의 사용 현황을 먼저 파악한 후, 상황에 맞게 리팩토링해야 한다.예외를 던지는 것은 비용이 많이 들 수 있으므로, 필요할 때만 신중하게 사용해야 한다.  private static boolean VALID = true; public boolean validateOrder(Order order) { if (order.isItemEmpty()) { throw new AppException("주문 항목이 없습니다."); } if (order.isTotalPriceLessThenZero()){ throw new AppException("올바르지 않은 총 가격입니다."); } if (order.hasNotCustomerInfo()) { throw new AppException("사용자 정보가 없습니다."); } return VALID; }Day 7 미션 피드백물론 리뷰를 할 때, 말을 조심스럽게 해야할 테지만, 코드 리뷰의 목적은 다 같이 좋은 코드를 만들어 보자는 좋은 의미입니다.코드 리뷰를 할 때는 인격적인 모독을 하지 않는다.코드 리뷰를 받을 때는 코드와 나를 동일시 하지 않는다.배운 점 정리 정적 메서드 팩토리를 만들 때는 생성자를 private으로 감춘다.null 대신 Empty 객체를 만들어서 처리한다.단수/복수를 신경 써서 변수명과 메서드명을 짓는다.리팩토링에는 정답이 없다. 하지만, 효율적인 구조와 다양한 의견이 있다. 

웹 개발클린코드

[인프런 워밍업 클럽 3기 CS전공지식] 2주차 발자국

1일차프로세스 동기화와 통신,재귀 프로세스 간 통신프로세스는 독립적으로 실행될 수도 있지만 다른 프로세스와 데이터를 주고받으며 통신하기도 합니다.통신 방식은 한 컴퓨터 내의 프로세스 간 통신과 네트워크를 통해 다른 컴퓨터의 프로세스와 통신하는 방법으로 나눌 수 있습니다. 프로세스 간 통신 방법파일(File) 이용통신하려는 프로세스들이 공통된 파일을 이용하여 데이터를 읽고 씁니다.파이프(Pipe) 이용운영체제가 생성한 파이프를 통해 데이터를 읽고 쓰는 방식입니다. 한 프로세스가 파이프에 데이터를 쓰면, 다른 프로세스가 이를 읽습니다. 쓰레드를 이용한 통신한 프로세스 내에서 쓰레드 간 통신 방법입니다.쓰레드는 코드, 데이터, 힙 영역을 공유하고, 각 쓰레드는 독립적인 스택을 가집니다. 따라서 공유된 데이터나 힙 영역을 통해 쓰레드 간 데이터를 주고받을 수 있습니다.네트워크를 이용한 통신소켓(Socket): 운영체제가 제공하는 소켓을 사용하여 네트워크 상의 다른 컴퓨터와 통신합니다.RPC(Remote Procedure Call, 원격 프로시저 호출): 네트워크를 통해 다른 컴퓨터의 프로세스에 있는 함수를 호출하는 방식입니다.  공유 자원과 임계 구역공유 자원프로세스 간 통신에서 공동으로 사용하는 변수나 파일 등을 공유 자원(Shared Resource) 이라고 합니다.공유 자원은 여러 프로세스가 동시에 접근할 경우, 접근 순서에 따라 결과가 달라질 수 있습니다.경쟁 조건 (Race Condition)여러 프로세스가 동시에 공유 자원에 접근하려고 경쟁하는 상황을 경쟁 조건이라 합니다.컨텍스트 스위칭(Context Switching)으로 인해 어떤 프로세스가 먼저 실행될지 예측하기 어렵습니다.따라서 여러 프로세스가 동시에 사용하면 안되는 영역을 정의했는데 이를 **임계구역이라 함**임계 구역 (Critical Section)여러 프로세스가 동시에 접근하면 안 되는 코드 영역을 임계 구역이라 합니다. 임계 구역 문제 해결 - 상호 배제 매커니즘(Mutual Exclusion)임계 구역 문제를 해결하려면 상호 배제 매커니즘이 필요합니다.주요 상호 배제 메커니즘뮤텍스(Mutex):상호 배제를 위한 동기화 도구로, 한 번에 하나의 프로세스 또는 스레드만 접근할 수 있도록 합니다.세마포어(Semaphore):공유 자원에 여러 프로세스가 동시에 접근하는 것을 방지하기 위한 동기화 도구입니다.모니터(Monitor):공유 자원과 해당 자원에 대한 연산을 묶어서 관리하는 동기화 도구입니다.상호 배제를 만족하기 위한 요구사항:상호 배제: 어떤 시점에도 하나의 프로세스만 임계 구역에 접근해야 합니다.진행 조건: 여러 프로세스가 임계 구역에 들어가기를 원하면, 하나의 프로세스만 선택되어야 합니다.유한 대기: 임계 구역에 들어간 프로세스는 최대한 빨리 종료해야 하며, 다른 프로세스가 무한정 기다리는 것을 방지해야 합니다.  프로세스 동기화 기법세마포어 (Semaphore)공유 자원에 여러 프로세스가 동시에 접근하는 것을 방지하기 위한 동기화 도구입니다.wait() 함수와 signal() 함수를 사용하여 자원 접근을 조절합니다.문제점: wait()와 signal()을 잘못 사용하면 **교착 상태(Deadlock)나 **경쟁 상태가 발생할 수 있습니다.**교착 상태 : 여러 프로세스가 서로 필요한 자원을 기다리면서 무한정 대기하는 상태입니다. 세마포어의 wait() 함수를 잘못 사용하면 교착 상태가 발생할 수 있습니다.**경쟁 상태 : 세마포어의 signal() 함수를 잘못 사용하면 여러 프로세스가 동시에 임계 구역에 접근하여 데이터 불일치 등의 문제가 발생할 수 있습니다. 세마포어의 사용 코드 예제(JAVA) public static void main(String[] args) throws InterruptedException { Semaphore semaphore = new Semaphore(1); semaphore.acquire(); // acqurie()는 wait()과 같은 역할을 담당 System.out.println("wait 실행"); semaphore.release(); // release()는 signal()과 같은 역할을 담당 System.out.println("signal 실행"); } 모니터 (Monitor)세마포어의 문제점을 보완한 상호 배제 매커니즘입니다.운영체제가 아닌 프로그래밍 언어 차원에서 제공하며, Java의 synchronized 키워드가 대표적입니다.synchronized 키워드가 붙은 메서드는 한 번에 하나의 쓰레드만 접근할 수 있습니다. 그럼 세마포어의 문제점을 보완한게 모니터니까 모니터를 사용하는게 가장 좋은건가??세마포어와 모니터: 세마포어는 개발자가 직접 'wait'과 'signal'을 조작해야 하기 떄문에 세밀한 관리가 가능하지만 호출 순서 및 위치를 잘못 설정하면 교착상태,경쟁조건이 생길 수 있어 복잡하고 세밀한 동기화를 사용하기 좋음모니터는 공유 자원에 접근하는 코드를 '모니터'라는 방 안에 넣으면 알아서 한 번에 한 명씩만 들어가도록 관리를 해줘 개발자는 안전하게 코드가 작성이 가능해 안전한 동기화에 좋지만 세마포어보다 오버헤드가 클 수 가있음모니터의 사용 코드 예제(JAVA) public static void main(String[] args) { Object lock = new Object(); // 락으로 사용할 객체 synchronized (lock) { // 락이 있을경우 해당 메서드에 진입해 실행 System.out.println("Inside synchronized block."); //실행문 종료 후 별다른 명령어 없이 자동으로 lock 반납 } }  뮤텍스 (Mutex)세마포어와 유사하지만, 뮤텍스는 오직 하나의 프로세스/스레드만 공유 자원에 접근하도록 제한합니다.잠금(lock)과 해제(unlock) 연산을 통해 임계 영역을 보호합니다.주로 하나의 공유 자원에 대한 접근을 순차적으로 처리해야 할 때 사용됩니다.뮤텍스는 잠금을 소유한 스레드만이 잠금을 해제할 수 있다는 특징이 있습니다.뮤텍스의 사용 코드 예제(JAVA) public static void main(String[] args) { ReentrantLock lock = new ReentrantLock(); lock.lock(); // lock 활성화 System.out.println("Lock acquired."); lock.unlock(); // lock 반납 System.out.println("Lock released."); }재귀 (Recursion)재귀란?어떤 것을 정의할 때 자기 자신을 참조하는 방식입니다.함수가 자기 자신을 호출하는 재귀 함수가 대표적인 예입니다.재귀 함수의 종료 조건재귀 함수에는 반드시 탈출 조건이 필요합니다.탈출 조건이 없으면 무한 재귀가 발생하며, **콜 스택(Call Stack)이 가득 차 프로그램이 비정상 종료됩니다.함수를 호출하면 해당 함수는 콜스택 위에 올라가고 함수가 종료되면 콜스택에서 제거됨**콜 스택 (Call Stack)함수 호출 기록을 저장하는 메모리 영역입니다.FILO(First In Last Out) 구조를 가지며, 마지막에 호출된 함수가 가장 먼저 종료됩니다. 재귀, 반복문반복문(for, while)으로 해결 가능한 문제를 재귀 함수로 구현하면 비효율적일 수 있습니다.재귀 함수는 함수를 호출할 때마다 새로운 **스택 프레임을 생성합니다. 이 스택 프레임에는 함수의 매개 변수지역 변수,반환 주소 등의 정보가 저장되어 코드만 실행하면 되는 반복문에 비해 함수 호출 및 스택 관리에 더 많은시간과 메모리를 소비해야함** 스택 프레임??프로그램 실행 중에 함수가 호출될 때 생성되는 메모리 블록으로 함수에 호출된 정보를 저장그러나 팩토리얼 계산등 문제의 구조 자체가 재귀적인 경우 재귀 함수가 유리합니다.// 반복문 int sum = 0; for (int i = 1; i <= n; i++) { sum += i; } // 재귀 함수 int recursiveSum(int n) { if (n == 1) { return 1; } return n + recursiveSum(n - 1); } 팩토리얼 예제(JAVA)public class FactorialExample { public static int factorial(int n) { if (n == 1) return 1; // 탈출 조건 return n * factorial(n - 1); } public static void main(String[] args) { System.out.println("5! = " + factorial(5)); // 120 출력 } }2일차프로세스 동기화와 통신,재귀 교착상태 (Deadlock)교착상태란?교착상태(Deadlock)는 여러 프로세스가 서로 다른 프로세스가 보유한 자원을 기다리며 무한히 대기하는 상태를 의미합니다. 이 상태에 빠지면 어떤 프로세스도 작업을 진행하지 못하게 됩니다.공유 자원: 프로세스들이 서로 공유하는 자원(예: 프린터, 메모리 등)이 있을 때 발생할 수 있음.독립 자원: 반대로, 공유하지 않는 자원만 사용한다면 교착상태는 발생하지 않음.  교착상태의 필요조건교착상태가 발생하기 위한 4가지 필요조건이 있으며, 이 중 하나라도 충족되지 않으면 교착상태가 발생하지 않습니다.상호 배제 (Mutual Exclusion)어떤 자원이 한 프로세스에 의해 점유되었다면, 그 자원은 다른 프로세스와 공유되지 않아야 함.비선점 (No Preemption)다른 프로세스가 점유한 자원을 강제로 빼앗을 수 없어야 함.점유와 대기 (Hold and Wait)어떤 프로세스가 자원을 점유한 상태에서 추가 자원을 기다려야 함.즉, 이미 할당된 자원을 보유하면서 다른 자원을 요청하고 대기하는 상태여야 합니다.원형 대기 (Circular Wait)점유와 대기 상태의 프로세스들이 원형으로 연결되어 있어야 함.예를 들어, 프로세스 A는 프로세스 B가 가진 자원을 기다리고, 프로세스 B는 프로세스 C가 가진 자원을 기다리고, 프로세스 C는 프로세스 A가 가진 자원을 기다리는 식입니다.  교착상태 해결 방법교착 상태는 여러 프로세스가 서로 필요한 자원을 기다리며 무한히 대기하는 상황을 의미합니다. 이러한 교착 상태를 해결하기 위한 다양한 방법 중 하나가 바로 **교착 상태 회피입니다.**교착 상태 회피란?시스템이 자원을 할당하기 전에 교착상태 가능성을 검사하고, 교착상태가 발생하지 않도록 자원을 할당.대표 알고리즘: **은행가 알고리즘 (Banker's Algorithm)** 은행가 알고리즘의 특징시스템은 프로세스들의 최대 요구 자원을 미리 알고 있어야 함.자원 할당 전에 시스템의 상태를 안정 상태(Safe State)와 불안정 상태(Unsafe State)로 구분안정 상태(Safe State): 교착상태에 빠지지 않을 수 있는 상태.불안정 상태(Unsafe State): 교착상태에 빠질 가능성이 있는 상태. (불안정 상태가 반드시 교착상태를 의미하진 않음)안정 상태의 예시시스템의 총 자원 14개P1: 최대 9개, 현재 5개 점유 → 추가로 4개 필요P2: 최대 6개, 현재 4개 점유 → 추가로 2개 필요P3: 최대 4개, 현재 3개 점유 → 추가로 1개 필요사용 가능한 자원: 2개자원 할당 순서P1은 4개를 요청하므로 거절.P2는 2개 요청 가능 → 작업 완료 후 6개 반납.P1은 이제 4개 요청 가능 → 작업 완료 후 자원 반납.불안정 상태의 예시시스템의 총 자원 10개P1: 최대 7개, 현재 5개 점유 → 추가로 2개 필요P2: 최대 5개, 현재 3개 점유 → 추가로 2개 필요P3: 최대 4개, 현재 2개 점유 → 추가로 2개 필요사용 가능한 자원: 1개자원 할당 상태P1, P2, P3 모두 추가 자원을 받기 위해 대기.사용 가능한 자원이 1개뿐이라 어느 프로세스도 필요한 자원을 다 받지 못함.세 프로세스가 서로 자원을 기다리며 무한 대기 상태 → 교착상태 발생  교착상태가 발생한지 알아내는 방법가벼운 교착상태 검출:타이머를 설정해 특정 시간 동안 프로세스가 작업을 하지 않으면 교착상태라고 판단.해결 방법 : 체크포인트를 통해 주기적으로 상태를 저장하고, 교착상태 발생 시 마지막 체크포인트로 롤백.간단하지만, 오탐지 가능성이 있고 롤백에 따른 오버헤드가 발생할 수 있습니다무거운 교착상태 검출:**자원 할당 그래프(Resource Allocation Graph, RAG)를 사용.프로세스-자원 관계를 그래프로 표현하고 사이클이 발견되면 교착상태로 간주.정확도가 높지만, 자원 할당 그래프를 관리하고 순환을 탐지하는 데 오버헤드가 발생합니다.오버헤드가 발생하지만, 프로세스를 강제로 종료하지 않고 교착상태를 해결 가능. **자원 할당 그래프자원할당 그래프의 사이클이 존재하는 지 그림으로 확인해당 그림처럼 프로세스와 자원 간의 사이클이 존재한다면 교착상태이고 해당 그림처럼 프로세스와 자원간의 사이클이 없으면 교착상태가 아니다하지만 사이클이 있어도 교착상태에 안걸리는 경우도 존재하는데P2와 P4가 작업을 완료하고 자원을 반납하면R1과 R2의 자원을 반납P1과 P3가 필요한 자원을 얻을 수 있음.원형 대기 상태가 깨지므로 교착상태가 해소됨.  재귀적으로 생각하기 패턴 1: 단순 반복의 재귀적 구현 (비효율적)반복문으로 간단하게 해결할 수 있는 문제를 재귀 함수로 구현하는 것은 일반적으로 비효율적입니다. 재귀 호출은 콜 스택 공간 사용을 발생시키므로 단순 반복 작업은 for 문을 사용하는 것이 좋습니다.// 반복문 (효율적) public static int sum1(int n) { int total = 0; for (int i = 1; i <= n; i++) { total += i; } return total; } // 재귀 함수 (비효율적) public static int sum2(int n) { if (n == 1) { return 1; } return n + sum2(n - 1); } 패턴 2: 하위 문제 기반 문제 해결 (하향식 vs. 상향식)재귀는 하위 문제의 결과를 기반으로 현재 문제를 해결하는 데 특히 유용합니다. 대표적인 예가 팩토리얼 계산입니다.재귀함수를 이용한 팩토리얼 자바코드 예시public static int factorial(int n) { if (n == 0) { return 1; } return n * factorial(n - 1); }for문을 이용한 팩토리얼 자바코드 예시public static int factorial(int n) { int result = 1; for (int i = 1; i <= n; i++) { result *= i; } return result; } 재귀와 반복문재귀 함수는 상향식 계산도 가능하지만, 반복문으로 구현하는 것보다 성능이 좋지 않습니다.재귀 함수는 하향식 계산에서 위력을 발휘합니다. 특히 문제의 구조 자체가 재귀적인 경우 재귀 함수를 사용하면 코드를 간결하고 이해하기 쉽게 만들 수 있습니다. 재귀와 for문을 이용해 배열의 합 구현// 재귀 함수 public static int arraySum(int[] arr, int n) { if (n == 0) { return 0; } return arr[n - 1] + arraySum(arr, n - 1); } // 반복문 public static int arraySum(int[] arr) { int total = 0; for (int num : arr) { total += num; } return total; } 문자열 길이 계산 public static int strLength(String arr) { if (arr == null || arr.isEmpty()) { return 0; } return strLength(arr.substring(1)) + 1; }  지수 함수 하향식 구현public static int power(int x, int y) { if (y == 0) { return 1; } return x * power(x, y - 1); } 3일차컴파일과 프로세스, 재귀 - 하노이 탑 컴파일과 프로세스프로그래밍 언어의 종류프로그래밍 언어는 컴파일 언어와 인터프리터 언어로 구분할 수 있습니다.컴파일 언어개발자가 코드를 작성하고 컴파일이라는 과정을 거쳐 기계어(0과 1) 로 된 실행 파일을 생성합니다.컴파일 과정에서 문법 오류가 있는지 검사하고, CPU에서 실행할 수 있는 기계어로 변환합니다.미리 실행 파일을 만들어 놓으므로 실행 속도가 빠릅니다.예시: C, C++, C# 등인터프리터 언어개발자가 작성한 코드를 미리 기계어로 변환하지 않고, 실행 시점에 한 줄씩 해석하며 실행합니다.실행 전에 오류 검사를 하지 않기 때문에 실행 중 오류가 발생할 수 있으며, 속도도 컴파일 언어보다 느립니다.예시: JavaScript, Python 등  컴파일 언어로 작성된 파일이 프로세스가 되기까지C 언어로 작성된 test.c 파일이 실행 파일이 되고, 최종적으로 프로세스가 되는 과정1. 컴파일 과정전처리컴파일러가 실행되면 가장 먼저 전처리기가 동작합니다.전처리기는 #include, #define 등 전처리 구문을 처리합니다.주석 제거를 수행한 후 전처리된 코드를 .i 파일로 저장합니다.컴파일전처리된 코드를 기계어에 가까운 어셈블리어로 변환합니다.이 과정에서 문법 오류 체크도 함께 이루어집니다.결과물은 어셈블리 코드가 담긴 .s 파일입니다.어셈블어셈블리어를 기계어 코드(오브젝트 코드)로 변환합니다.생성된 오브젝트 파일은 .o 확장자를 가집니다.이 오브젝트 파일에는 코드 영역과 데이터 영역이 나눠져 있습니다.링커오브젝트 파일은 실행 파일이 되기 위해 링커(Linker) 를 거칩니다.링커는 여러 오브젝트 파일을 하나로 합치고, 코드 영역과 데이터 영역을 하나로 묶습니다. 결과물은 실행 파일(.exe)입니다. 2. 프로세스 생성 과정실행 파일이 프로세스가 되려면 다음 과정을 거칩니다프로그램 실행사용자가 .exe 파일을 실행합니다.운영체제가 프로세스를 생성합니다.메모리 할당운영체제는 실행 파일에 있는 코드 영역과 데이터 영역을 가져와 프로세스의 코드 영역과 데이터 영역에 로드합니다.그리고 빈 스택(Stack)과 힙(Heap) 영역도 할당합니다.PCB 생성운영체제는 PCB를 생성하여 프로세스를 관리합니다.PCB에는 프로세스 상태, 프로그램 카운터(PC), CPU 레지스터, 메모리 정보 등이 저장됩니다.프로그램 카운터 설정프로그램 카운터(PC) 는 다음 실행할 명령어의 주소를 가리킵니다.처음에는 코드 영역의 첫 번째 명령어의 주소를 가리킵니다.CPU 스케줄링운영체제의 CPU 스케줄러가 프로세스를 준비 상태에 놓고, CPU를 할당받으면 실행 상태로 전환되어 명령어가 실행됩니다.   재귀-하노이탑자바 코드 사용 예시 public static void hanoi(int count, String from, String to, String temp) { if (count == 0) { return; } hanoi(count - 1, from, temp, to); System.out.println(count + "를 " + from + "에서 " + to + "로 이동"); hanoi(count - 1, temp, to, from); } public static void main(String[] args) { hanoi(3, "A", "C", "B"); // 3은 옮겨야할 갯수 A는 시작위치 C는 도착해야할 위치 B는 거쳐갈 위치 }결과창4일차메모리 관리,버블정렬과 선택정렬 메모리의 종류레지스터가장 빠른 기억 장소로 CPU 내부에 존재합니다.컴퓨터의 전원이 꺼지면 데이터가 사라지므로 휘발성 메모리입니다.CPU의 32비트, 64비트는 레지스터 크기를 의미합니다.CPU는 연산 시 메인 메모리(RAM)에 있는 데이터를 레지스터로 가져와 계산하고, 결과를 다시 메인 메모리에 저장합니다. 캐시(Cache)레지스터와 메인 메모리 사이에 위치하여, 데이터 접근 속도를 향상시키는 역할을 합니다.CPU는 필요한 데이터를 메인 메모리에서 가져오기 전에 L1 캐시 → L2 캐시 → L3 캐시 순으로 확인합니다.캐시에 가져올 데이터가 없으면 메인 메모리에서 데이터를 가져옵니다. 메인 메모리(RAM) 실제 운영체제와 프로세스가 로드되는 공간입니다.휘발성 메모리로, 전원이 꺼지면 데이터가 사라집니다.속도는 HDD/SSD보다 빠르지만, 가격이 비싸기 때문에 실행 중인 프로그램만 저장합니다. HDD/SSD비휘발성 메모리로, 전원이 꺼져도 데이터가 보존됩니다.속도는 메인 메모리보다 느리지만, 가격이 저렴하여 데이터 저장 용도로 사용됩니다.  메모리 할당 방식메모리 오버레이:프로그램을 분할하여 필요한 부분만 메모리에 로드하고, 나머지는 스왑 영역에 저장합니다.스왑(Swap): 스왑영역에 있는 데이터를 메모리로 가져오고 메모리에 있는 데이터를 스왑영역으로 옮기는것을 말합니다.가변 분할 방식(Variable Partitioning)프로세스 크기에 따라 메모리를 연속된 공간에 동적으로 할당합니다.장점: 연속된 공간으로 할당하기 때문에 내부 단편화가 발생하지 않습니다.단점: 외부 단편화가 발생합니다.예시: 프로세스 A(50MB), B(30MB), C(15MB), D(10MB)가 메모리에 차례로 로드이후 A와 D가 종료되어 빈 공간(50MB + 10MB)이 생기고 새로운 프로세스 E(60MB)를 로드하려고 할 때 이 공간들은 연속되어 있지 않아 할당할 수 없는데 이를 외부 단편화라고 하며, 조각 모음을 통해 빈 공간들을 하나로 합쳐야 합니다. 이 과정은 메모리에 있는 프로세스를 일시 중지하고 공간을 이동시키는 작업이므로 오버헤드가 발생합니다. 고정 분할 방식(Fixed Partitioning)메모리를 고정 크기 블록으로 나눠 프로세스를 할당합니다.장점: 구현이 간단하고 오버헤드가 적습니다.단점: 프로세스 크기와 상관없이 고정된 크기로 메모리를 할당하므로 내부 단편화가 발생합니다.예시: 메모리를 2MB로 나눈다 가정할 때 프로세스 A(1MB), B(2MB), C(5MB)를 할당할 경우,A는 2MB 블록에 할당되어 1MB의 내부 단편화가 발생합니다. B는 2MB 블록에 할당되고 B는 2MB이기 때문에 정확히 떨어짐. C는 총 5MB이기 때문에 2MB 블록을 3개의 구역에 나눠서 저장해 1MB의 내부 단편화가 발생. 버디 시스템(Buddy System)가변 분할 방식과 고정 분할 방식을 혼합하여 단점을 보완합니다.메모리를 2의 제곱수 크기로 나누고, 필요할 때마다 나눠 프로세스를 할당합니다.장점: 외부 단편화를 방지하고, 메모리 합병(병합)이 간단합니다.단점: 약간의 내부 단편화가 발생할 수 있습니다. 그럼 메모리 할당 방식은 둘의 단점을 최소화한 버디 시스템이 최고인가??버디 시스템의 내부 단편화와 작은 크기의 블럭을 지나치게 나누면 생기는 메모리 관리 오버헤드가 커질수있어 최신 운영체제는 페이징 기법을 활용하거나, 세그멘테이션과 조합해서 사용페이징 기법 (Paging)정의:메인 메모리를 고정된 크기의 블록인**페이지 프레임(Page Frame)으로 나누고, 프로세스의 가상 메모리 공간도 동일한 크기의 **페이지(Page)로 나눕니다.프로세스의 페이지들은 메모리의 페이지 프레임에 비연속적으로 할당됩니다.페이지 프레임과 페이지란???페이지는 프로세스를 나눈 조각페이지 프레임은 메모리를 나눈 조각장점:외부 단편화가 발생하지 않습니다.메모리 관리가 단순해집니다.단점:내부 단편화가 발생할 수 있습니다. (마지막 페이지가 완전히 채워지지 않는 경우)예시:프로세스 A가 5KB이고, 페이지 크기가 2KB라면, A는 3개의 페이지로 나뉩니다.마지막 페이지는 1KB만 사용되고 1KB는 낭비되므로 내부 단편화가 발생합니다.추가 설명: 페이지 테이블을 사용하여 가상 주소를 물리 주소로 변환합니다.고정 분할 방식과 페이징 기법은 비슷하지만 고정 분할 방식은 메모리를 나눈 후 프로세스를 맞춰 넣는 방식이고,페이징은 프로세스를 나눈 후 메모리에 퍼즐처럼 넣는 방식  세그멘테이션 (Segmentation)정의:프로세스를 논리적인 의미 단위인 세그먼트(Segment)로 나누어 메모리에 로드합니다.세그먼트의 크기는 가변적입니다.장점:논리적으로 관련된 코드나 데이터가 한 곳에 모이므로 효율적입니다.프로그램의 구조를 반영하여 메모리를 관리할 수 있습니다.단점:외부 단편화가 발생할 수 있습니다.예시:프로그램이 코드(10KB), 데이터(5KB), 스택(8KB)으로 나뉜 경우, 각 부분이 독립적인 세그먼트로 관리됩니다.추가 설명:세그먼테이션은 프로그램의 논리적인 구조를 반영하여 메모리를 관리하는 데 유용합니다.최근에는 페이징과 함께 사용되어 메모리 보호 및 공유 기능을 강화합니다.요약:페이징은 메모리를 고정 크기로 나누어 관리하고, 세그멘테이션은 논리적인 단위로 나누어 관리합니다.페이징은 외부 단편화를 해결하고, 세그멘테이션은 프로그램의 논리적인 구조를 반영합니다.. 페이징 + 세그먼테이션을 사용하면 내부/외부 단편화가 완전히 사라지는 것은 아니지만, 각각의 단점을 최소화할 수 있습니다.   버블 정렬과 선택 정렬정렬 알고리즘은 데이터를 특정 순서로 나열하는 방법을 정의버블 정렬과 선택 정렬은 가장 기본적인 정렬 알고리즘으로 이해와 구현이 쉽지만 성능은 다소 아쉬움1. 버블 정렬 (Bubble Sort)원리:인접한 두 원소를 비교하여 순서가 맞지 않으면 교환하는 과정을 반복합니다. 예시:[4, 2, 3, 1] 배열을 정렬하는 과정:[2, 4, 3, 1] (4와 2 교환)[2, 3, 4, 1] (4와 3 교환)[2, 3, 1, 4] (4와 1 교환)위 과정을 반복하여 [1, 2, 3, 4] 완성성능:시간 복잡도: O(n²) - 데이터가 많아질수록 비교 횟수가 제곱으로 증가합니다.장단점:장점: 이해와 구현이 매우 쉽습니다.단점: 성능이 좋지 않아 대규모 데이터 처리에는 부적합합니다. 자바코드로 예시 구현 public static void bubble(int[] arr) { int n = arr.length; for (int i = 0; i < n - 1; i++) { // arr 배열의 길이에서 -1만큼만 반복 for (int j = 0; j < n - i - 1; j++) { if (arr[j] > arr[j + 1]) { // arr[j]번쨰 값이 arr[j]다음의 값보다 크다면 int temp = arr[j]; // 현재 arr[j]번째의 값을 저장해두고 arr[j] = arr[j + 1]; // arr[j]의 값을 arr[j] 다음의값으로 변경 arr[j + 1] = temp;// arr[j] 다음의값을 이전에 temp에 저장해둔 arr[j]값으로 변경 } } } } public static void main(String[] args) { int[] arr = {4, 2, 3, 1}; bubble(arr); for (int num : arr) { System.out.print(num + " "); } }    2. 선택 정렬 (Selection Sort)원리:배열에서 가장 작은 원소를 찾아 첫 번째 위치로 이동시키고, 다음으로 작은 원소를 찾아 두 번째 위치로 이동시키는 과정을 반복합니다.정렬되지 않은 부분에서 최소값을 선택하여 정렬된 부분의 마지막 위치로 이동시킵니다.예시:[4, 2, 1, 3] 배열을 정렬하는 과정:[1, 2, 4, 3] (최소값 1을 첫 번째 위치로 이동 기존에 있던 4는 1이있던 자리로 이동)[1, 2, 4, 3] (다음 최소값 2는 이미 정렬된 위치)[1, 2, 3, 4] (다음 최소값 3을 세 번째 위치로 이동)성능:시간 복잡도: O(n²) - 버블 정렬과 마찬가지로 성능이 좋지 않습니다.장단점:장점: 버블 정렬과 마찬가지로 이해와 구현이 쉽습니다.단점: 성능이 좋지 않아 대규모 데이터 처리에는 부적합합니다. 자바코드로 예시 구현 public static void selection(int[] arr) { for (int i = 0; i < arr.length - 1; i++) { // 정렬되지 않은 부분의 첫 번째 원소를 최소값으로 가정 // 정렬된 부분은 반복에서 제거하기위해 사용 int minValueIndex = i; // 정렬되지 않은 부분에서 최소값을 찾음 for (int j = i + 1; j < arr.length; j++) { if (arr[j] < arr[minValueIndex]) {// arr[j]값이 arr[minValueIndex] 값보다 작을경우 minValueIndex = j; // 정렬할 인덱스 값을 넣어주고 } } // 최소값을 정렬되지 않은 부분의 첫 번째 원소와 교환 int temp = arr[i]; // 현재 arr[i]의 값을 temp에 저장 arr[i] = arr[minValueIndex]; // arr[i]의 값을 arr[minValueIndex] 변경 arr[minValueIndex] = temp; // arr[minValueIndex]값을 이전에 temp에 저장해둔 값으로 변경 } } public static void main(String[] args) { int[] arr = {4, 2, 1, 3}; selection(arr); System.out.print("실행 결과 :"); for (int num : arr) { System.out.print(num + " "); } }  정리버블 정렬과 선택 정렬은 기본적이지만 비효율적인 정렬 알고리즘입니다.  2주차 회고 잘한 점저번주에 다짐했던 그날그날 강의를 듣고 정리하는건 잘지켰고 확실히 그날 배운 내용을 바로바로 정리하니 저번주보다 죽은지식이 덜했습니다.저번주는 몰아서 정리하다보니 아 이거 뭐였지 하며 다시 찾아보고 하는 경우가 많았는데 현저히 줄어든게 느껴졌습니다. 아쉬운 점운영체제는 역시 여전히 어려웠습니다.아무래도 강의가 기본으로 쉽게 이해하려는데 초점이 맞춰져있다보니 아주 쉽게 동작원리를 알려주다보니 하나의 내용을 던져주고 궁금해서 그걸 가지고 파다보면 끝도없는 용어와 또 다른 내용이 튀어나와 역시 쉽지않았습니다.자료구조와 알고리즘은 재귀 버블정렬 등 해당 알고리즘의 개념을 이해만 한다면 코드로 녹여내는건 생각보다 어렵지않았고 오히려 정말 재밌게 공부했었습니다.이무래도 코드를 작성하면 시각적으로 결과가 바로바로 보여지다보니 좀더 흥미를 느꼈던거같습니다. 개선할 점이제 다음주가 마지막 주차인데 이번주 보다 공부 시간을 좀 더 늘리겠습니다. 목표이번주 역시 로드맵에 맞게 해당 강의 영상을 듣고 바로바로 발자국으로 내용 정리하기2주차에 투자한 공부시간보다 더 늘리기질문 더 많이하기   

Yang HyeonBin

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

2주차에 배운 내용을 정리해본다.깃허브 링크 1. 배운 내용1. Next.js에서 메타데이터 정의하기<meta> 태그를 이용해 사이트 정보를 정의하려면,서버 컴포넌트 파일에서Metadata를 정의해줘야 함// page.tsx import { Metadata } from "next"; import Ui from "./Ui"; // 페이지의 메타데이터를 정의 // use client에서는 사용 불가 - 클라이언트 코드는 Ui.tsx에서 정의하는 이유 export const metadata: Metadata = { title: "Dropbox Clone", description: "A minimalist Dropbox Clone", }; export default function Home() { return <Ui />; } 2. 파일 드랍 존 만들기<input /> 태그를 이용, type="file"3. supabase1. storage bucket 만들기업로드 가능한 파일 종류 설정 가능만들 때 Allowed MIME types 옵션에서 image/* 등의 조건을 추가하면 됨2. policy 생성사이드바 policy 메뉴에서 생성 가능이름, 가능한 액션 종류 선택, 누구에게 가능하게 할지 선택 가능4. 파일 드래그앤드롭 - react-dropzone 라이브러리 사용사용법은 npm 공식 문서의 코드 조각을 확인https://www.npmjs.com/package/react-dropzone  2. 이슈 사항1. storage의 get url 형태 변경: getImageUrl 함수 커스텀 어려움이미지의 만료일을 지정할 수 있게 변경됨그러면서 token이라는 서치 파라미터가 필수값으로 추가된 듯토큰을 누락한 형태로 확인 시 에러가 발생하며 이미지 로드 실패{"statusCode":"400","error":"Error","message":"querystring must have required property 'token'"} bucket을 public으로 전환하고, supabase에서 제공하는 getPublicUrl 메서드를 사용storage에서 bucket 이름 옆 드롭다운 메뉴 → edit bucket → public으로 설정 getImageUrl 함수 내부를 아래와 같이 수정const { data } = supabase.storage .from(process.env.NEXT_PUBLIC_STORAGE_BUCKET!) .getPublicUrl(path); /** * A simple convenience function to get the URL for an asset in a public bucket. If you do not want to use this function, you can construct the public URL by concatenating the bucket URL with the path to the asset. * This function does not verify if the bucket is public. If a public URL is created for a bucket which is not public, you will not be able to download the asset. * * @param path The path and name of the file to generate the public URL for. For example `folder/image.png`. * @param options.download Triggers the file as a download if set to true. Set this parameter as the name of the file if you want to trigger the download with a different filename. * @param options.transform Transform the asset before serving it to the client. */ getPublicUrl( path: string, options?: { download?: string | boolean; transform?: TransformOptions } ): { data: { publicUrl: string } } { const _path = this._getFinalPath(path) const _queryString = [] const downloadQueryParam = options?.download ? `download=${options.download === true ? '' : options.download}` : '' if (downloadQueryParam !== '') { _queryString.push(downloadQueryParam) } const wantsTransformation = typeof options?.transform !== 'undefined' const renderPath = wantsTransformation ? 'render/image' : 'object' const transformationQuery = this.transformOptsToQueryString(options?.transform || {}) if (transformationQuery !== '') { _queryString.push(transformationQuery) } let queryString = _queryString.join('&') if (queryString !== '') { queryString = `?${queryString}` } return { data: { publicUrl: encodeURI(`${this.url}/${renderPath}/public/${_path}${queryString}`) }, } } StorageFileApi.ts를 참고하면 다양한 메서드가 있음 - createSingedUrl를 이용하면 expiresIn을 직접 지정 가능. 이걸 이용하면 bucket이 public이 아니어도 가능할듯 /** * Creates a signed URL. Use a signed URL to share a file for a fixed amount of time. * * @param path The file path, including the current file name. For example `folder/image.png`. * @param expiresIn The number of seconds until the signed URL expires. For example, `60` for a URL which is valid for one minute. * @param options.download triggers the file as a download if set to true. Set this parameter as the name of the file if you want to trigger the download with a different filename. * @param options.transform Transform the asset before serving it to the client. */ async createSignedUrl( path: string, expiresIn: number, options?: { download?: string | boolean; transform?: TransformOptions } ): Promise< | { data: { signedUrl: string } error: null } | { data: null error: StorageError } > { try { let _path = this._getFinalPath(path) let data = await post( this.fetch, `${this.url}/object/sign/${_path}`, { expiresIn, ...(options?.transform ? { transform: options.transform } : {}) }, { headers: this.headers } ) const downloadQueryParam = options?.download ? `&download=${options.download === true ? '' : options.download}` : '' const signedUrl = encodeURI(`${this.url}${data.signedURL}${downloadQueryParam}`) data = { signedUrl } return { data, error: null } } catch (error) { if (isStorageError(error)) { return { data: null, error } } throw error } } /** * Creates multiple signed URLs. Use a signed URL to share a file for a fixed amount of time. * * @param paths The file paths to be downloaded, including the current file names. For example `['folder/image.png', 'folder2/image2.png']`. * @param expiresIn The number of seconds until the signed URLs expire. For example, `60` for URLs which are valid for one minute. * @param options.download triggers the file as a download if set to true. Set this parameter as the name of the file if you want to trigger the download with a different filename. */ async createSignedUrls( paths: string[], expiresIn: number, options?: { download: string | boolean } ): Promise< | { data: { error: string | null; path: string | null; signedUrl: string }[] error: null } | { data: null error: StorageError } > { try { const data = await post( this.fetch, `${this.url}/object/sign/${this.bucketId}`, { expiresIn, paths }, { headers: this.headers } ) const downloadQueryParam = options?.download ? `&download=${options.download === true ? '' : options.download}` : '' return { data: data.map((datum: { signedURL: string }) => ({ ...datum, signedUrl: datum.signedURL ? encodeURI(`${this.url}${datum.signedURL}${downloadQueryParam}`) : null, })), error: null, } } catch (error) { if (isStorageError(error)) { return { data: null, error } } throw error } } 2. server action 파일에서 console.log는 개발자 도구가 아닌 터미널에 찍힌다는 사실..storageActions.ts에 정의한 uploadFile 함수를 수정하고 제대로 데이터가 들어가는지 확인하려고 console.log를 사용아무리 해도 useQuery까지는 잘 로그가 찍히는데, uploadFile에 작성한 로그가 브라우저 개발자도구에 전혀 찍히지 않아 뭐가 문제인지 한참 헤맴코드 에디터 터미널에 찍히고 있었음.. ㅠㅠ3. 파일명에 한글이 포함될 경우 업로드 안되는 오류디스코드에 다른 러너분들이 공유해준 정보에 따르면 supabase storage는 AWS의 S3 스토리지와 호횐되어, 파일명도 AWS S3의 Safe charaters - 영문, 숫자, 일부 특수기호 만 허용한다고 함어떻게 저장할까?base64 인코딩을 통해 S3-safe한 이름으로 변경해 저장하면 업로드 가능⇒ 저장 및 다운로드, 이름 표시하는 코드에서 인코딩/디코딩 함수를 사용하게 변경 완료어떻게 검색할까?인코딩/디코딩된 값으로 검색 호환이 안됨⇒ db에 저장해야 함db에 저장하도록 변경했는데, 한글 초성만 검색됨..todo-list 검색 때와의 차이가 뭐길래 안되는거지? 3. 미션 - 파일의 마지막 수정(업로드) 시간 표시하기storage를 확인하면, Added on, Last modified 정보가 있음file이 어떤 형태인지 로그 찍어 확인file.updated_at 키에 저장된 값임을 확인, 이 값을 사용추가로 file.metadata에 파일 타입과 사이즈 등의 정보도 있어 그 정보들도 적절히 표시하도록 함 (ui는 supabase storage를 참고함)

웹 개발Next.jsSupabaseReact.jsFile

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기] 백엔드 코드 - 박우빈 발자국 2주차

강의 수강 노션 링크https://www.notion.so/DAY06-1b2010f075ca80d09e08d4dd35376dd5?pvs=4https://www.notion.so/DAY07-1b3010f075ca80b4945cd230929481ff?pvs=4https://www.notion.so/DAY09-1b4010f075ca805f8e1ec01a7c00b16c?pvs=4https://www.notion.so/DAY10-1b4010f075ca80a7a731da0aa16b8e43?pvs=4출처[워밍업클럽 리더블코드 ][워밍업클럽 테스트코드] 👣발자국2주차👣🏫배운 내용 🏫주석의 양면성자주 변하는 정보는 최대한 주석 사용 지양하기우리가 가진 모든 표현방법을 총 동원해서 → 코드에 녹여 → 주석 사용 지지뢰찾기 리팩토링게임의 상태를 주석으로 설정하는 대신 → ENUM으로 관리그래서 외부에서 호출해서 사용하기무한루프 반복 구조는 위험특정 상황에서만 반복문 돌도록 바꿔줘야한다.지뢰찾기 같은 케이스에서는 게임이 “진행중” 일때만 반복문 돌 수 있도록 처리하기변수와 메서드의 나열 순서💡상태변경 메서드 >> 판별 메서드 >> 조회 메서드 순으로 나열 하자이때, 메서드 우선순위는 공개 메서드에서 private메서드 순으로 내려와야한다.  자동테스트?그동안 내가 학원에서 배워온 건, 수동테스트 였나보다스프링부트에서 애너테이션을 활용하여 수동사냥만 해왔던 것..인가?그리고 단위로 /unit으로 쪼개서 (메소드,클래스별로) 테스트를 진행 ⇒ 단위테스트그러다보니 검증속도도 빠르고 안정적임  JUnit이란?단위 테스트를 위한 프레임워크 → 퀜트백 프레임워크풍부한API제공해주는 프레임워크로 테스트 코드 작성해보자   JUnit vs assertJ 두개의 차이점AssertJ 의 장점자연어 가까워 가독성이 좋다체이닝 방식이 가능함JUni5의 단점assertEquals는 단순히 "Expected: A, Actual: B" 결과물만 추출함기능이 단순하고 제한적  assertJ 다양한 메서드기본적인 검증 isEqualTo() : 두 값이 같은지 비교 isNull() : 값이 null인지 비교 isTrue() : 값이 true인지 비교컬렉션 검증 hasSize() : 컬렉션의 크기 비교 —> 리스트의 사이즈 찾기 contains() : 컬렉션에 특정 요소가 포함되어 있는지 확인 isEmpty() : 컬렉션이 비어 있는지 확인 🎶경계값 조건 * 정수가 3이상일때, A라는 조건 만족해야함.해피케이스경계값 활용하기 !즉, 3에대한 테스트를 짜보자.5에 대해는 만족하지만, 3이 만족이 안될 수 있잖아 !예외 케이스2로 조건값보다 더 아래쪽 범위로 테스트 하기❗인사이트❗칭찬깃 사용법에 대해 좀더 연구하고, 프로젝트에 적용하며 강의를 따라가려고 노력하였다.깃에 대해 전혀 몰랐던 사람으로,,, 개발 공부하기 위해선 깃 활용이 무척이나 중요하다는 걸 깨달았다.코드리팩토링시 강의를 보며 , 강사님이 로직 처리를 하는 한단계 한단계씩 끊어서 정리하였다.테스트코드 진행시, 테스트하기 어려운 부분을(요구사항에 맞게 테스트 로직을 짰는데, 그 요구사항이 개발하는 시점의 요구사항이랑 충돌이 될때 )잘 이해하고 숙지하며 이 로직에 대해선 따로 분리하여 테스트 코드 관리하는 시야가 필요하다. 아쉬움그러나, 동영상 일시정지를 하고 노션에 정리한다고 한들, 온전히 내것이 되는가? 아쉬움이 남아있다.이상태로 다시 한번 해보세요~ 주어지면 , 아무것도 못한다.내 스스로 코드를 짜보는 학습이 필요할 것 같다.이번 미션11 코드 제출도 어디서 어떻게 시작 해야 할지 막막하다 앞으로 어떻게?지금도 지뢰찾기 코드 마스터 하지도 않고, 미션 제출도 선생님 코드 따라치기만 했었다. 지뢰찾기 로직을 파악하기엔 내 머리가 아직 준비가 안되었고, 내 마음의 여유가 준비되지 않은 상태였다. 현재 국비학원 졸업작품으로 팀프로젝트를 지난주에 시작 하다보니, 우선순위는 팀포폴이다. 그래서 시간을 내어 지뢰찾기 코드를 마스터 하기에는 조금은 어려울 듯 하여 ,,,, 팀포폴이 어느정도 마무리가 되어가면 그때 지뢰찾기 자바 코드 눈에 익히고 리팩토링 수업을 다시 들으며 공부를 해야 할 것 같다는 생각이 든다.   🧑🏻‍💻두번째 중간점검 나의 코드를 다른분 코드와 비교해보자 !미션4 미션 공통 피드백static정적 메소드는 빼자 ( 인텔리제이 단축키 사용한 사람 적.발) 풀스택 취업 준비백엔드의 매력은 ?눈에 예쁘게 보이는거 좋아하는데 → 프론트 개발자도 고민 → 프론트 앤드를 어느정도 잘 할줄 아는 백앤드 개발자가 되기로 함성향상 잘 맞을 것 같았다. 복잡한 방식을 여러 방법으로 접근 할 수 있는게 성향이 잘 맞았다. 따라치기만 하는 지금 상황어려움 보다는 익숙함의 문제이다 . 어려움건 10%일뿐 .익숙하지 않아서 거부감이 드는 것 일뿐,진짜 어려운건 아님 . 석박사 해야지 알수있는 정도는 아님메타인지 및 의도적으로 수련하는 것이 가장 빠르고 명확하다 💡될때까지 반복해라.💡계층구조 패키지 나누기 기준이란?도메인중심 ( 유저, 히스토리, 오더 )도메인별로 관심사가 명확해짐프로젝트가 커서, 도메인별로 떨어져야한다 → 아주 좋아유저가 회원이라는 도메인이 정말 중요해서 떼어야함 → 아주 유리 하다컨트롤러,서비스 계층들이 각각 저 도메인별로 나누어져 있다보니, 패턴이 달라질 수 있음공통기능이 멀리 떨어져 있으니, 공통기능이 중복으로 생성 될 우려가 있음 레이어 중심 ( 컨트롤러,서비스,모델)한눈에 레이어러 보기 좋아.도메인간 결합도가 증가해서 MSA전환이 불리하다 💡작은 프로젝트이면 레이어중심이 좋다💡큰 프로젝트는 도메인 중심으로 잡자. 개발 면접a 먼저 개발 지식 질문 CS기초, 스프링등b 이력서 기반 질문인성 질문 (개발에 대한 태도 )질문의 빈도는 A>B>C그러나 C가 별로이면 무조건 탈락취업준비에 대해회사가 원하는 기술들 JD가 무엇인지 공통적으로 찾고 있는 기술스택이 무엇인지 찾아보기그리고, 그 회사만이 찾고있는 기술,팀 도메인이 무엇인지 챙겨보기예상질문리스트 검색해서 → Interview Question Driven 취준 하기

백엔드박우빈워밍업클럽클린코드백엔드발자국

강동훈

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

💻 운영체제📌 프로세스 동기화프로세스는 다른 프로세스와 데이터를 주고 받으며 통신한다.여러 프로세스가 공유자원에 접근하여 데이터를 수정하고 읽을 경우, 동기화 문제가 발생할 수 있다.공유 자원 : 프로세스 간 공유되는 변수나 파일임계 구역: 여러 프로세스가 동시에 사용하면 안되는 구역경쟁 조건: 공유 자원을 서로 사용하기 위해 경쟁하는 것상호배제의 요구사항임계 구역에는 하나의 프로세스만 접근할 수 있다여러 요청에도 하나의 프로세스만 접근 가능하다임계 구역에 들어간 프로세스는 최대한 빠르게 나와야 한다.상호배제 메커니즘세마포어세마포어 변수를 갖고있는 프로세스가 먼저 실행되고 작업이 완료되면 signal()을 통해 변수 반환세마포어 변수가 없는 프로세스는 대기 wait()세마포어 변수를 할당받으면 프로세스 실행 가능공유 자원 수에 따라 세마포어의 수 증가모니터운영체제의 차원이 아닌 프로그래밍 언어에서 처리자바에서 synchronized가 붙은 함수가 실행되면 다른 프로세스가 접근 불가함수를 임계 구역에 감싸지 않아도 되어 편리하게 구현 가능 📌 데드락교착상태(데드락): 여러 프로세스가 서로 다른 프로세스의 작업이 끝나기를 기다리다가 아무도 작업을 진행하지 못하는 상태필요조건1. 상호배제: 한 프로세스가 리소스를 점유하였다면 다른 프로세스 접근 불가2. 비선점: 점유중인 프로세스의 리소스를 뺏어갈 수 없음3. 점유와 대기: 한 프로세스는 리소스를 점유하고 있는 상태에서 다른 프로세스의 리소스 점유를 대기해야 함4. 원형대기: 점유와 대기를 하는 프로세스들의 관계가 원형이어야 함.회피교착상태는 발생할 수 밖에 없다 > 발생의 원인을 줄이거나 빠르게 해결 안정상태 (시스템 총 자원이 14라 가정)현재 총 12개의 작업이 할당되었다.P1이 4개의 자원을 요청하면 사용 가능한 자원이 2(14-12)개 남아있기에 거절P2가 2개의 자원 요청하면 사용 가능한 자원 2개 할당P2의 작업이 마무리되면 사용 가능한 자원 6개로 증가나머지 프로세스들의 요청 예상 자원 커버 가능 불안정상태현재 사용 가능 자원은 1(14-13)개이다.모든 프로세스들의 요청 예상 자원을 할당해줄 수 없다.모든 프로세스들이 최대 자원을 요구하지 않는다면 교착 상태에 빠지지 않을 수 있지만가능성이 높다 가벼운 교착상태 검출 : 타이머 이용. 프로세스가 일정 시간동안 작업을 진행하지 않는다면 교착상태로 검출 일정시간마다 체크포인트 생성, 교착상태 예상 시에 롤백무거운 교착상태 검출: '자원 할당 그래프'를 통해 프로세스에 할당된 자원을 모니터링교착상태를 일으킨 프로세스 강제종료체크포인트로 롤백  📌 메모리1. 레지스터: 가장 빠른 기억 저장소이자 휘발성 메모리. 메인 메모리 데이터를 CPU레지스터를 가져와 연산 후 계산 결과를 다시 메인 메모리에 저장2. 캐시: 메인 메모리에서 필요할 것 같은 데이터를 미리 캐시에 저장3. 메인메모리(RAM): 실제 운영체제와 프로세스들이 저장되는 휘발성 메모리4. 보조저장장치: 비휘발성 메모리이며 프로그램, 파일을 저장 물리 주소(절대 주소): 메모리 공간의 실제 주소논리 주소(상대 주소): 사용자가 다루는 메모리 주소1. 사용자가 프로그램 실행 - 사용자 입장에서는 0x0000 메모리로 작업2. 프로세스는 실제 물리주소 0x4000 주소에 저장3. 사용자가 0x0100 주소의 데이터 요청 (논리 주소)4. 논리 주소(0x0100)와 재배치 레지스터(실행 중인 프로세스의 물리주소 - 0x4000)을 더해서 0x4100의 값을 전달 메모리 할당 방식❓ 유니 프로그래밍 환경에서는 하나의 프로세스만 메모리에서 동작이 가능하다. 만약 메모리의 용량을 초과한 프로그램을 실행시키려면?✅ 스왑 과정이 존재하기 때문에 실제 메모리에 전체 프로세스가 올라가있는 것에 비하면 속도가 느리다.메모리 오버레이: 실행시킬 프로세스를 분할시켜, 사용되는 부분을 메모리에 올리고 나머지는 하드디스크 스왑 영역에 저장 ❓ 멀티 프로그래밍 환경에서는 어떻게 동작?1. 가변 분할 방식(세그멘테이션): 프로세스가 크기에 따라 메모리를 분리- 연속된 메모리 공간에 할당되기에 내부 단편화 현상 없음- 외부 단편화 발생: 연속된 공간에 할당을 해야하니 5MB 프로그램을 3MB와 2MB 공간에 할당이 불가하다.2. 고정 분할 방식(페이징): 프로세스 크기 상관없이 메모리 분리 (만약 5MB 프로그램을 2MB 고정 분할 메모리에 저장시키려면 2 / 2 / 1로 분리) -> 비연속 메모리 할당- 구현이 간단하고 오버헤드가 적음- 내부 단편화 발생: 위 예시에서 2MB 분할 공간에 1MB만 사용3. 버디 시스템(가변 + 고정): 2의 승수로 메모리를 분할하여 할당- 전체 메모리 영역을 하나의 프로세스가 올라갈 수 있을 정도로 2의 승수로 나눠서 분할- 최소한의 내부 단편화, 간단한 메모리 합치기 조각 모음 : 외부 단편화에서 분리된 메모리 공간을 합치는 작업 - 실행 중인 프로세스를 일시적으로 멈춰야 해서 오버헤드 발생 📌 더 찾아본 점IPC(Interprocess Communication)란?프로세스 간 협업을 위해 데이터를 공유하기 위해서는 IPC 기술이 필요하다 공유 메모리(Shared Memory): 공유 메모리 지역을 지정하고 프로세스들은 공유 메모리에 접근하여 데이터를 공유한다. (producer-consumer problem)메세지 패싱(Message Passing): 협력하는 프로세스 간 메세지 전달을 통해 데이터 동기화(communication link)를 통해서 각 프로세스가 소통한다. race condition이란?다수의 프로세스(혹은 쓰레드)가 같은 데이터를 동시에 접근하거나 처리하면, 실행되는 순서에 따라서 결과가 달라진다.이를 해결하려면 특정 시간에 하나의 프로세스만 공유 자원을 다뤄야 한다. 즉, 프로세스는 동기적으로 실행되어야 한다.1. 상호배제(Mutual Exclusion)을 보장해주어야 한다.- 한 프로세스가 "임계영역(citical section)"을 실행 중일 때, 다른 프로세스는 임계 영역을 실행할 수 없다.2. 데드락(deadlock)을 회피(진행)- 임계 영역에 들어갈 프로세스를 정하는 건, 임계 영역에 들어가야하는 프로세스들만 참여할 수 있다. - 영역에 들어가는 과정이 무한정 지연되는 것을 방지3. 유한 대기(Bounded Waiting) (starving 기아 상태 방지)- 임계 영역에 들어가기를 요청한 프로세스는 무한정 기다리면 안된다. 상호배제 메커니즘Mutex Locks: 임계 영역에 진입하면 lock을 acquire()와 release()를 통해 주고 받음Semaphore: 공유자원 수용가능 수에 따라 정수형 변수 s 변수를 초기화. wait(s)와 signal(s)를 통해 각 작업에서 s값을 중가, 차감 자원할당 그래프(Resource0Allocation Graph)운영체제에서 자원이 할당되어 있는 상태를 그래프로 그려, 시각적으로 데드락의 여부를 판단- T = {T1, T2, ..., Tn}: 실행 중인 쓰레드- R = {R1, R2, ..., Rm}: 할당될 자원 타입- T_i → R_j: i 쓰레드가 j 자원을 요청한다- R_j → T_i: j 자원을 i 쓰레드에 할당되어 있다.위 예제에서는 3개의 쓰레드와 4개의 자원이 있으며 2개의 순환 구조를 갖고 있다.T1 → R1 → T2 → R3 → T3 → R2 → T1T2 → R3 → T3 → R2 → T2모든 쓰레드는 하나의 자원을 점유 중인 상태에서 다음 자원을 점유하기 위해 대기 상태에 있고, 이러한 상태가 원형을 이루어지고 있기 때문에 데드락에 빠질 확률이 높다고 판단한다. 메모리 주소 바인딩1. symbolic address: 소스 프로그램에 변수와 같이 메모리 주소를 상징적으로 저장2. relocatable address: 실제 메모리 주소가 결정될 수 있는 재배치 가능 주소3. absolute address: 컴파일 타임에 결정되는 메모리 주소 1. compile time: 컴파일 시점에 물리 주소가 결정 (`absolute address`로 결정)2. load time: 컴파일 시점에 메모리 위치가 확정나지 않았다면, relocatable code로 변환. 프로세스가 로드되어 메모리에 올라갈 때 물리 주소가 결정3. execution time: 프로그램 실행 도중에 메모리 주소가 변경될 수 있다면 런타임까지 바인딩 지연. MMU에 의해서 논리 주소를 물리 주소 결정 📌 백엔드 면접 질문해보기데드락과 라이브락의 차이점?데드락은 두 개 이상의 쓰레드가 자원을 점유하고 있지만 다른 자원을 점유하기 위해 대기 중인 교착 상태를 의미합니다. 라이브락이란 두 개 이상의 쓰레드가 충돌을 회피하기 위해 실패 작업을 반복하지만 진전이 없는 상태를 의미합니다.데드락은 교착 상태에서 모든 프로세스가 멈춰버리지만 라이브락은 실패 작업을 계속 시도하기 때문에 멈추지는 않습니다. 락 기반 동기화와 락 프리 알고리즘이란?락 기반 동기화란 하나의 프로세스만 공유자원을 사용하도록 강제하여 동기화시키는 방법입니다. 뮤텍스나 세마포어가 락 기반 동기화에 해당됩니다. 락 기반 기법은 두 개 이상의 스레드가 서로 상대방의 락의 해제를 기다린다면 데드락이 발생할 수 있으며, 낮은 우선순위의 스레드가 자원을 먼저 점유하면 높은 우선순위의 스레드가 대기를 해야하는 우선순위 역전 등의 문제가 있습니다.락 프리 알고리즘이란 락을 사용하지 않고도 동기화를 유지시킬 수 있는 알고리즘입니다. 해당 알고리즘은 CAS(Compare And Swap) 같은 원자적 연산을 통해 동기화를 보장하는데, 원자적 연산이란 연산이 중간에 중단없이 시작되지 않거나 완전히 수행되는 것(all or nothing)을 의미하며 CAS는 해당 원자적 연산을 기반으로 현재값을 읽고 예상했던 값과 일치할 경우에만 값을 변경하며 일치하지 않는다면(다른 쓰레드의 개입) 업데이트하지 않는 방식입니다. Node.js에서 데드락이 발생할 수 있을까? 공식 문서에 따르면 Node.js에는 락이 존재하지 않아서 데드락이 발생될 확률이 매우 적다고 적혀있습니다. 다만 libuv에서 제공하는 동기적인 메서드를 실행할 때, js파일에 대해 대기 상태가 발생할 수 있는데 이는 데드락이라기 보다는 Blocking에 해당합니다. 또한 이러한 동기 I/O 메서드에 대해서도 콜백을 포함한 비동기 처리 함수가 함께 제공됩니다. (readFileSync / readFile) V8 엔진 메모리 관리 방식은? V8 엔진은 node.js의 실행 엔진이며 컴파일과 GC를 포함하고 있다.V8 메모리 구조(Resident set)은 크게 Heap과 Stack 메모리로 구분된다.Heap에는 크게 New Space와 Old Space가 존재하며 New Space는 짧은 생명주기를 갖는 새로 생성된 객체들이 저장되며 2번의 minor GC에도 제거되지 않은 객체는 Old Space로 이동되어 저장된다. New Space는 minor GC, Old Space는 Major GC로 참조되지 않는 변수들을 가비지 컬렉팅 한다. Buffer와 Stream의 메모리 사용 방식 차이? Buffer: 데이터를 조각(chunk)내어 buffer에 다 차우면(buffering) 일괄로 데이터를 전송. - 메모리의 사용 비율이 크지만 데이터 처리 속도가 크게 향상Stream: 데이터 청크와 버퍼의 크기를 작게하여 지속적으로 데이터를 전달하는 방식 - 메모리 사용이 적지만 데이터 실시간 효율성이 높아짐 📔 회고🚀 최종 목표 : 더 효율적인 백엔드 개발을 위해 기본적인 운영체제 지식들을 확실히 잡아가기🚀 매주 규칙:각 섹션마다 하나의 .md 파일을 생성하고, 섹션 내 각 유닛은 헤더로 구분강의를 듣고 최대한 이쁘게 (?) 정리해놓기 매 강의를 듣고 궁금하거나 이해가 안가는 부분은 추가적으로 더 찾아서 정리해두기매 강의 내용을 백엔드 관점에서 고민해보고 GPT와 대화를 통해 정리첫 주차 발자국과 미션을 수행한 후에 이번 주에는 더 구체적이고 집중적으로 강의를 듣고 복습할 수 있게 방식을 수정하였다.규칙 3번에서 매 강의를 듣고 궁금하거나 이해가 안가는 부분에 대해서는 보통 검색을 통해 블로그를 찾아보고 정리하였었다. 하지만 블로그에 적혀있는 글마다 내용이 다르기도 하고 궁금증이 해소되지 않는 부분들이 있어, 더 찾아보던 도중 CS로 유명한 공룡책(Operating Systerm Concepts) 무료 영문 pdf파일을 발견하였고 그 날 강의에 해당하는 주제에 맞춰 공룡책을 공부하고 개념을 탄탄히 하는 것에 집중하였다. 추가적으로 인프런 강의 중에 무료로 공룡책에 대한 내용을 설명해주는 강의도 있어 함께 공부를 하니 더 깊이 있고 쉽게 개념을 잡아갈 수 있었던 것 같다.규칙 4번에서 처음에는 백엔드 관점으로 강의 내용을 살펴보려 하였는데, 생각보다 모호하고 막연하다고 생각하여 규칙을 조금 변경하였다. GPT에는 Node.js 전문가 프롬프트가 제공되어 있어서, 해당 프롬프트를 이용하여 오늘 배운 내용에 대해 이야기하고 백엔드 면접 질문을 리스트로 달라고 요청하면 기초부터 고급 그리고 node.js를 활용한 심화 질문까지 리스트업해준다. 해당 부분에 대해서 스스로 답변해보고 답변하지 못한 부분이나 설명이 부족한 부분은 다시 AI와 대화하면서 지식을 채울 수 있었던 것 같다.결과적으로 3. 매 강의 듣고 해당 내용 공룡책 & 강의 1회 정독 및 수강 / 4. 매 강의에 대한 내용 백엔드 면접 질문을 추려서 답변하고 개념 구체화하기 이렇게 수정하여 2주차를 진행하였다. 기존 독학으로 CS를 공부하였을 때는, 그 범위가 너무 넓어 어떤 부분을 공부하고 있는지도, 어디까지 공부해야할 지도 전혀 감을 잡을 수 없었는데 이렇게 강의를 수강하고 그 강의의 깊이있는 개념까지 찾아가며 공부하다보니 확실히 체계가 잡히고 개념도 탄탄히 공부해 볼 수 있는 것 같다. 해당 방식으로 공부하면 딱 몰입할 수 있을만큼 적당히 공부하는 것 같아서 다음 주에는 추가적인 규칙을 넣기보다 기존 규칙을 유지해보려고 한다. 

시스템 · 운영체제인프런워밍업운영체제발자국

채채

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

학습 내용운영체제프로세스 동기화컴퓨터 내 프로세스 간 통신동일 프로세스 내 통신: 운영체제가 생성한 파이프로 데이터를 읽고 씀스레드 간 통신: 데이터 영역이나 힙 영역 사용 시 통신 가능동일 네트워크 내 프로세스 간 통신소켓 통신, RPC(원격 프로시저 호출)공유 자원: 프로세스 통신 시 공동으로 이용하는 변수나 파일임계구역 (Critical Section): 여러 프로세스가 동시에 사용하는 영역해결책: 상호 배제의 매커니즘경쟁 조건 (Race Condition): 공유 자원을 서로 사용하기 위해 경쟁하는 것상호 배제의 요구사항임계 영역에는 동시에 하나의 프로세스만 접근여러 요청에도 하나의 프로세스의 접근만 허용임 영역에 들어간 프로세스는 빠르게 나와야함.세마포어공유자원이 하나 이상일 때 처리하는 동기화 방법예) 프린터 실을 예시로 들때직원: 프로세스기존 프린터: 공유자원프린터실: 임계구역대기줄: 대기큐열쇠 관리자: 운영체제열쇠: 세마포어자바스크립트는 멀티 스레드 환경이 아니라서 비동기 코드에서 적용할 수 있음모니터:세마포어의 단점을 해결한 상호배제 매커니즘교착 상태 (데드락)여러 프로세스가 서로 다른 프로세스의 작업을 기다리다가 아무도 작업을 진행하지 못하는 상태필요 조건 (모두 충족 시 교착 상태 발생)상호 배제: A가 리소스 점유 시 B에게 공유될 수 없음 (즉, 한 번에 하나의 프로세스만 특정 자원을 사용할 수 있음)비선점: A 프로세스는 B가 점유한 리소스를 뺏을 수 없음점유와 대기: 리소스 A를 가지고 있는 상태에서 리소스 B를 원하는 상태 (무한정 대기)원형 대기: 점유와 대기를 하는 프로세스가 원형을 이룸 (서로 교차해서 자원을 기다리고 있는 상태, 모든 프로세스가 영원히 자원을 획득할 수 없음)회피 방법은행원 알고리즘: 교착 상태를 피하기 위해 상태 확인 뒤 실행안정 상태: 자원 요청을 했으나 사용 가능한 자원이 더 적을 경우 요청 거부불안정 상태: 모든 프로세스에 자원을 공급할 수 없는 경우교착상태 검출가벼운 교착 상태 검출: 일정시간동안 작업하지 않을 시 교착 상태 발생으로 간주과정: 일정 시간마다 체크포인트 생성 -> 작업 저장 -> 타임 아웃으로 교착 상태 발생 시 마지막 지점으로 롤백무거운 교착 상태 검출: 운영체제가 자원 사용 여부를 지켜보고 교착 상태 발생 시 해결과정: 자원 할당 그래프 사용 -> 교착 상태 발생 시 순환 구조 형성 -> 교착 상태 해결을 위해 순환 구조 단절 -> 해결 후 재실행 시 체크포인트 롤백컴파일 과정: 문법 오류 확인, CPU에서 처리 가능한 기계어로 실행 파일 생성 => 속도 빠름대표 언어: C, C++, C#인터프리터 과정: 코드를 한 줄씩 해석 후 변환 (미리 검사하지 않음) => 속도 느림, 오류 발생 가능성 높음대표 언어: JavaScript, Python, Ruby1. test.c → 전처리기 → 전처리 구문 처리 → test.i → 컴파일러로 어셈블리어로 변환 → test.s → 어셈블러로 오브젝트 파일 변환 → test.o → 링커에서 실행 파일로 생성 → test.exe2. test.exe 프로그램 실행 → 운영체제가 프로세스 생성 → PCB 생성 → 프로그램 카운터를 코드 영역의 첫번째 주소로 설정 → CPU 스케줄링에 따라 프로세스 실행 → 종료메모리 종류: 레지스터, 캐시, 메인메모리(RAM), 보조저장장치(HDD, SSD)레지스터가장 빠른 기억장소, CPU 내 존재, 휘발성 메모리, 빠름RAM의 값을 레지스터로 가져와서 계산 -> 계산 결과는 다시 RAM에 저장캐시레지스터에서 쓸 법한 데이터를 RAM에서 미리 가져와서 저장, 사용 완료 후 다시 RAM에 저장보조 저장장치: 가격 저렴, 비휘발성 메모리휘발성 메모리: 전원 공급이 없으면 데이터가 사라짐비휘발성 메모리: 전원 공급이 없어도 데이터가 사라지지 않음메인 메모리 RAM운영체제에 프로세스가 올라가는 공간, 휘발성 메모리, 가격 비쌈멀티 프로그래밍 환경에서 여러 프로그램을 올리다 보니 복잡해짐운영체제는 메모리 관리를 위해 1바이트 구역으로 나누고 숫자(주소)를 매김32bit 메모리레지스터 크기, 산술논리연산장치, 데이터 이동 버스 === 32bit가능한 메모리: 2³² ⇒ 4GB64bit 메모리레지스터 크기, 산술논리연산장치, 데이터 이동 버스 === 64bit가능한 메모리: 2⁶⁴ ⇒ 거의 무한대32bit에 비해 한 번에 처리할 수 있는 양이 많음 → 속도가 더 빠름물리주소 공간(절대 주소): 0x0번지부터 시작하는 주소공간메모리 관리자가 바라본 실제 주소논리 주소 공간(상대 주소): 사용자 관점에서 바라본 주소, 물리주소를 몰라도 접근 가능메모리 어디선가 실행되겠지~~하고 컴파일러는 0번지에서 실행한다고 가정하고 컴파일함경계 레지스터: 하드웨어적으로 운영체제 공간과 사용자 공간 분리CPU 내 존재사용자 프로세스가 경계 레지스터의 값을 벗어났는지 감시 (메모리 관리자)벗어났을 경우 프로세스 종료유니 프로그래밍에서의 메모리 분리당장 실행해야하는 부분만 잘라서 메모리에 올리고 나머지는 하드디스크의 스왑 영역에 저장스왑: 스왑영역에 있는 데이터 일부를 메모리로 가져오고 메모리에 있는 데이터를 스왑영역으로 이동멀티 프로그래밍에서의 메모리 분리가변 분할 방식 (Segmentation)프로세스의 크기에 따라 메모리를 나누는 방식연속 메모리 할당: 프로세스가 연속된 메모리 공간에 할당장점: 낭비 공간인 내부 단편화가 없음단점: 외부 단편화 발생고정 분할 방식 (Paging)메모리를 정해진 크기로 나누는 방식예) 고정 크기가 2MB인 경우 5MB인 프로세스는 2MB + 2MB + 1MB + 낭비 공간 1MB비연속 메모리 할당: 프로세스가 여러 개로 쪼개어 여러 곳에 할당됨장점: 구현 간단, 오버헤드 적음단점: 낭비 공간 내부 단편화 발생현대 운영체제는 가변 분할 방식과 고정 분할 방식을 혼합함외부 단편화메모리의 공간보다 프로세스의 크기가 더 큰 경우해결책: 조각 모음단점: 현재 사용 중인 프로세스 전체 일시 중지, 메모리 공간을 이동시키는 작업을 해야함 ⇒ 오버헤드 발생내부 단편화메모리 공간보다 프로세스의 크기가 더 작은 경우해결책: 없음버디 시스템: 2의 승수로 메모리 분할 및 할당방법2의 승수로 프로세스 크기보다 작은 값이 나올때 까지 분리 (예: 1024 - 1024(512 - 512(256 - 256)))비슷한 크기의 프로세스에 할당 (예: 512byte에 할당 (내부 단편화가 적게 발생, 실행이 끝나고 근접한 메모리와 합치기 쉬움))2의 승수로 분할했기 때문에 조립만 하면 큰 공간이 만들어짐 (조각모음보다 간단함)장점가변 분할: 프로세스 크기에 따라 할당되는 메모리 크기가 다름외부 단편화 방지를 위해 메모리 공간 확보가 간단함내부 단편화가 발생하나 많은 공간이 낭비되지 않음  자료구조 & 알고리즘재귀 함수: 자기 자신을 호출하여 문제를 해결하는 함수필수: 기저 조건기저 조건이 없으면 반복되는 출력으로 호출 스택의 메모리 공간이 가득 차서 자동으로 종료됨기본 패턴단순 반복 계산 (예: 누적합, 누적곱, DFS 등)하위 문제의 결과를 기반으로 현재 문제 계산 (예: 팩토리얼, 피보나치 등)상향식 계산: for문, 재귀 함수하향식 계산: 재귀 함수만 가능호출 스택함수가 호출되면서 올라가는 메모리 영역, FILO재귀함수는 모든 호출이 콜 스택에 올라가고 for문은 1개만 올라감예) 팩토리얼function factorial(number) { if (number <= 1) return 1; return number * factorial(number - 1) } console.log(factorial(4)) // 4 * 3 * 2 * 1예) 모든 원소의 합function sumArray(arr){ if (arr.length == 1) return arr[0]; return sumArray(arr.slice(0, -1)) + arr[arr.length - 1] } const arr = [1,2,3,4,5] const sum = sumArray(arr); console.log(sum)예) 하노이 탑규칙1. 한 번에 하나의 원반을 움직일 수 있다.2. 가장 위에 있는 원반만 옮길 수 있다3. 아래에 작은 원반이 올 수 있다.function hanoi(count, start, target, tmp) { if (count === 0) return; hanoi(count - 1, start, tmp, target) hanoi(count - 1, tmp, target, start) } hanoi(3, 'A', 'C', 'B')1. count - 1개의 작은 원반을 start → tmp로 옮긴다. (임시: target)2. 가장 큰 원반을 start → target으로 옮긴다.3. count - 1개의 작은 원반을 tmp → target으로 옮긴다 (임시: start)4. 이전 과정을 반복하며 모든 원반을 start에서 target으로 옮긴다.5. 모든 원반을 옮겨 count가 0이 되면 재귀를 종료한다.버블 정렬장점: 구현하기 쉬움단점: 성능이 좋지 않음방법: 근접한 두 개의 숫자를 비교하여 정렬되어있지 않다면 오름차/내림차 순으로 정렬한다.시간 복잡도: O(n²)버블 정렬 구현function bubbleSort(arr) { for (let i = 0; i < arr.length - 1; i++) { for (let j = 0; j < arr.length - i - 1; j++) { if (arr[j] > arr[j + 1]) { let temp = arr[j] arr[j] = arr[j + 1] arr[j + 1] = temp } } } } const arr = [4, 2, 3, 1] // ===== 정렬 전 ===== console.log(arr) // ==== 정렬 후 ===== console.log(bubbleSort(arr)선택 정렬장점: 구현하기 쉬움단점: 성능이 좋지 않음방법: 정렬되지 않은 영역의 첫번째 원소와 가장 작은 값의 위치 교환시간 복잡도: O(n²)선택 정렬 구현function selectionSort(arr) { for (let i = 0; i < arr.length - 1; i++) { let minValueIndex = i for (let j = i + 1; j < arr.length; j++) { if (arr[j] < arr[minValueIndex]) { minValueIndex = j } } let temp = arr[i] arr[i] = arr[minValueIndex] arr[minValueIndex] = temp; } }  회고Keep시간표에 따라 매일 CS 지식을 학습했다. 뿌듯나머지 한 주를 잘 마무리하면 좋겠다.Problem없다Try재귀를 구현할 때 더 작은 문제로 나누는 연습을 해야할 것 같다. 기저 조건과 점화식은 바로 이해되는데 나머지가 약간 알쏭달쏭하다.

워밍업클럽운영체제알고리즘자료구조

이재준

[한국산업기술협회 세미나 및 박람회] 스마트공장 SMT/PCB 불량유형별 분석대책 실무

SMT/PCB 품질 확보, 중요한 과제!!!불량 유형별 개선사례를 우리 기업에 적용 가능할까?다른 기업은 품질관리를 어떻게 하고 있을까? 최근 SMT 및 PCB 제조 공정에서다양한 불랑 사례가 지속적으로 보고 되고 있습니다!!!(솔더 브릿지, 부품미삽, 비아 홀 불량, 레이어 간 단락 등) 제조 공정서 발생하는 불량 줄이고, 신뢰성 높이는 것이기업 경쟁력의 핵심 요소!!! 이에 한국산업기술협회는SMT 및 PCB 최신 불량 유형 및 원인을 분석하고,효과적인 해결방안을 논의하며,생산성과 품질 향상 및 원가절감을 위한실질적인 인사이트를 제공하기 위해 세미나를 기획했습니다. 그리고 세미나에 참가하면"2025 스마트 SMT&PCB 어셈블리 박람회" 무료 참관 가능하다는 사실!!! 빠른 대안을 기획하고 운영하는 자만이미래를 선도할 수 있습니다. 많은 관심과 신청 부탁드립니다. 일시 및 장소 : 2025. 4. 3.(목), 10:00~17:00 / 수원컨벤션센터 106호사전등록 : 2025. 4. 2.(수) 까지참가비 : 200,000원 (사전등록) / 250,000원 (당일 현장 접수)참가비 할인 : 180,000원 (1업체 3인이상 접수시)신청 문의 : 02-6959-5562 / puma1708@kitanet.or.kr사전등록신청 : (구글폼) https://forms.gle/GmNgrLNAJa3sjDEYA세미나 등록 혜택 : "2025 스마트 SMT&PCB 어셈블리 박람회 무료 참관"기타 안내 : 교재, 강연파일, 다과, 중식 제공

반도체SMTPCB스마트SMTPCB어셈블리박람회세미나한국산업기술협회SMTPCB불량유형별분석대책실무품질확보품질관리원가절감

tikitaka

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

[풀스택 완성] Supabase로 웹사이트 3개 클론하기 (Next.js 14) - 로펀 2주차는 Supabase Storage를 어떻게 사용하는지 학습했다. Supabase Storage 설정Supabase > DashBoard > Project > Storage버킷 생성하기Name of Bucket: 버킷 이름Public bucket: trueAdditional configuration>Allowed MIME types: image/* (이미지만 허용)Policies>For Full customizationPolicy name: 정책 이름Allowed operation: 누구나 CRUD를 할 수 있기 때문에 모두 허용Target roles: anon으로 누구나 허용Review 클릭>Save policy 클릭CRUD 각각에 대한 policies 생성server action에서 storage 접근하기export async function uploadFile(formData: FormData) { const supabase = await createServerSupabaseClient(); const files = Array.from(formData.entries()).map( ([name, file]) => file as File ); const results = await Promise.all( files.map((file) => supabase.storage .from(process.env.NEXT_PUBLIC_STORAGE_BUCKET) .upload(file.name, file, { upsert: true }) ) ); return results; }Server Action을 사용하여 Supabase Storage에 파일을 업로드하는 함수이다. FormData를 받아 파일을 읽고, Supabase Storage에 업로드하는 방식으로 동작한다. const supabase = await createServerSupabaseClient();서버 측에서 Supabase 클라이언트를 생성하여 Storage API를 사용할 수 있도록 한다. const files = Array.from(formData.entries()).map( ([name, file]) => file as File );FormData에서 모든 항목을 배열로 추출하여 File 객체로 변환하여 파일을 추출한다.const results = await Promise.all( files.map((file) => supabase.storage .from(process.env.NEXT_PUBLIC_STORAGE_BUCKET) .upload(file.name, file, { upsert: true }) ) ); return results;Promise.all()을 사용하여 여러 개의 파일을 동시에 업로드한다.supabase.storage.from(bucket).upload(filename, file, options)을 통해 지정한 버킷에 파일을 업로드한다.{ upsert: true }: 동일한 이름의 파일이 있을 경우 덮어쓴다. (insert + update) 업로드 된 결과를 반환한다. 2주차 미션github: https://github.com/thayoon/nextjs-supabase-dropbox-cloneDropbox Clone 프로젝트에 파일의 마지막 수정(업로드) 시간을 표시하는 기능을 추가하세요.파일 목록에서 각 파일의 “마지막 수정 시간”을 표시📌 참고 문서: Supabase Storage - 파일 목록 가져오기미션 해결 방법:list() 응답값 확인Supabase의 list() 함수를 사용하면 파일 정보를 가져올 수 있다.참고 문서에서 확인한 응답값은 다음과 같다:{ "data": [ { "name": "avatar1.png", "id": "e668cf7f-821b-4a2f-9dce-7dfa5dd1cfd2", "updated_at": "2024-05-22T23:06:05.580Z", "created_at": "2024-05-22T23:04:34.443Z", "last_accessed_at": "2024-05-22T23:04:34.443Z", "metadata": { "eTag": "\"c5e8c553235d9af30ef4f6e280790b92\"", "size": 32175, "mimetype": "image/png", "cacheControl": "max-age=3600", "lastModified": "2024-05-22T23:06:05.574Z", "contentLength": 32175, "httpStatusCode": 200 } } ], "error": null }이 중에서 updated_at이 파일의 마지막 수정 시간을 나타낸다. Server Action에서 list() 호출 및 데이터 반환actions/storageActions.tsexport async function searchFiles(search: string = "") { const supabase = await createServerSupabaseClient(); const { data, error } = await supabase.storage .from(process.env.NEXT_PUBLIC_STORAGE_BUCKET) .list(null, { sortBy: { column: "updated_at", order: "desc" }, search, }); handleError(error); return data; }파일 목록을 가져오는 searchFiles() 함수를 구현한다.참고 문서를 통해 sortBy 옵션을 적용하여 updated_at 을 기준으로 내림차순 정렬하여 최신 파일이 먼저 오도록 설정한다. 클라이언트 컴포넌트에서 데이터 가져오기components/dropbox-image-list.tsx "use client"; import { useQuery, useMutation } from "@tanstack/react-query"; export default function DropboxImageList({ searchInput }) { const searchImageQuery = useQuery({ queryKey: ["images", searchInput], queryFn: () => searchFiles(searchInput), }); return ( <section className="grid lg:grid-cols-4 md:grid-cols-3 sm:grid-cols-2 grid-cols-1 gap-2"> {searchImageQuery.isLoading && <Spinner />} {searchImageQuery.data && searchImageQuery.data.map((image) => ( <DropboxImage key={image.id} image={image} /> )} </section> ); } useQuery를 사용해 서버에서 데이터를 가져온다.가져온 데이터를 DropboxImage 컴포넌트로 전달한다. 마지막 수정 시간 표시components/dropbox-images.tsx"use client"; import { IconButton, Spinner, Checkbox } from "@material-tailwind/react"; import { getImageUrl } from "utils/supabase/storage"; export default function DropboxImage({ image }) { // 마지막 수정 시간 한국 시간 변환 const updated = new Date(image.updated_at) .toLocaleString("en-CA", { year: "numeric", month: "2-digit", day: "2-digit", hour: "2-digit", minute: "2-digit", hour12: false, timeZone: "Asia/Seoul", }) .replace(",", ""); return ( <div className="relative w-full flex flex-col gap-2 p-4 border border-gray-100 rounded-2xl shadow-md"> {/* Image */} {/* fileName */} {/* update time */} <p className="flex justify-end text-xs text-gray-500"> 마지막 수정: {updated} </p> {/* trash Button */} </div> ); } updated_at 값을 toLocaleString()을 사용해 한국 시간으로 변환하고 화면에 표시한다.추가 구현 사항긴 파일명 생략 표시<div className="truncate">{image.name}</div>className에 truncate를 적용하여 긴 파일명을 한 줄로 표시하고 넘칠 경우 "..."으로 생략한다. 사진 업로드 오름차순/내림차순 정렬처음에는 사용자의 정렬 방식 선택에 따라 서버에서 데이터를 다시 호출하도록 구현했지만, 비효율적이라고 판단하여 클라이언트에서 정렬을 처리하는 방식으로 변경했다.1차 시도 - 서버에서 정렬된 데이터 요청actions/storageAction.tsexport async function searchFiles(search: string = "", isLatest) { const supabase = await createServerSupabaseClient(); const { data, error } = await supabase.storage .from(process.env.NEXT_PUBLIC_STORAGE_BUCKET) .list(null, { sortBy: { column: "updated_at", order: isLatest ? "desc" : "asc" }, search, }); handleError(error); return data; } isLatest 값에 따라 정렬 순서를 desc(최신순) 또는 asc(오래된순)으로 설정사용자가 정렬 방식을 변경할 때마다 서버 요청이 발생하여 비효율적이다.2차 시도 - 클라이언트에서 정렬 처리components/dropbox-image-list.tsx"use client"; import { Spinner, Menu, MenuHandler, MenuList, MenuItem, Button, Typography, } from "@material-tailwind/react"; import { useState } from "react"; import { useQuery } from "@tanstack/react-query"; const sortMenu = [ { title: "최신순", isLatest: true }, { title: "오래된순", isLatest: false }, ]; export default function DropboxImageList({ searchInput }) { const [openMenu, setOpenMenu] = useState(false); const [isLatest, setIsLatest] = useState(true); const searchImageQuery = useQuery({ queryKey: ["images", searchInput], queryFn: () => searchFiles(searchInput), }); return ( <div> {/* 정렬 버튼 */} <div className="flex flex-row-reverse"> <Menu open={openMenu} handler={setOpenMenu} allowHover> <MenuHandler> <Button variant="text" className="flex items-center gap-1 align-middle text-base font-normal capitalize tracking-normal" > {isLatest ? "최신순" : "오래된순"} <i className={`fas fa-angle-down transition-transform ${ openMenu ? "rotate-180" : "" }`} /> </Button> </MenuHandler> <MenuList className="hidden gap-3 overflow-visible lg:grid"> <ul className="flex w-full flex-col gap-1"> {sortMenu.map(({ title, isLatest }) => ( <MenuItem key={title} onClick={() => setIsLatest(isLatest)}> <Typography variant="h6" color="blue-gray" className="mb-1"> {title} </Typography> </MenuItem> ))} </ul> </MenuList> </Menu> </div> {/* 이미지 리스트 */} <section className="grid lg:grid-cols-4 md:grid-cols-3 sm:grid-cols-2 grid-cols-1 gap-2"> {searchImageQuery.isLoading && <Spinner />} {searchImageQuery.data && (isLatest ? searchImageQuery.data.map((image) => ( <DropboxImage key={image.id} image={image} /> )) : searchImageQuery.data .slice() .reverse() .map((image) => <DropboxImage key={image.id} image={image} />))} </section> </div> ); }서버 요청은 기본적으로 최신순으로 설정하고, 클라이언트에서 데이터를 reverse()하여 정렬을 변경하는 방식으로 개선했다. 사진 다중 삭제사진을 다중 선택하여 삭제하는 기능은 체크박스를 활용해 구현했다.사용자는 "전체 선택" 및 "선택 삭제" 기능을 통해 한 번에 여러 사진을 삭제할 수 있다.components/dropbox-image-list.tsx "use client"; export default function DropboxImageList({ searchInput }) { // ... const [allSelected, setAllSelected] = useState(false); const [isSelected, setIsSelected] = useState([]); const searchImageQuery = useQuery({ queryKey: ["images", searchInput], queryFn: () => searchFiles(searchInput), }); const deleteFileMutation = useMutation({ mutationFn: deleteFile, onSuccess: () => { searchImageQuery.refetch(); }, }); function handleChecked(isChecked) { setAllSelected(isChecked); if (isChecked && searchImageQuery.data) { setIsSelected(searchImageQuery.data.map((image) => image.name)); } else { setIsSelected([]); } } return ( <div role="section"> <div className="flex justify-between"> <div className="flex justify-center items-center gap-3"> <Checkbox color="blue" label={ <Typography> 전체 선택 ({isSelected.length}/ {searchImageQuery.data && searchImageQuery.data.length} {!searchImageQuery.data && 0}) </Typography> } checked={allSelected} onChange={(e) => handleChecked(e.target.checked)} /> <Button className="rounded-full" size="sm" variant="outlined" color={isSelected.length > 0 ? "blue" : "gray"} disabled={isSelected.length > 0 ? false : true} onClick={() => { setIsSelected([]); setAllSelected(false); deleteFileMutation.mutate(isSelected); }} > {deleteFileMutation.isPending ? <Spinner /> : "선택 삭제"} </Button> </div> // ... </div> <section className="grid lg:grid-cols-4 md:grid-cols-3 sm:grid-cols-2 grid-cols-1 gap-2"> {searchImageQuery.isLoading && <Spinner />} {searchImageQuery.data && searchImageQuery.data.map((image) => ( <DropboxImage key={image.id} image={image} isSelected={isSelected} setIsSelected={setIsSelected} setAllSelected={setAllSelected} totalLength={searchImageQuery.data.length} /> ))} </section> </div> ); } 전체 선택: 사용자가 "전체 선택" 체크박스를 클릭하면, 모든 이미지가 선택된다.선택된 이미지 수와 총 이미지 수가 표시된다.선택 삭제: 사용자가 선택한 이미지들을 삭제할 수 있는 "선택 삭제" 버튼을 제공한다. 이미지가 선택되었을 때만 활성화된다.상태 관리: isSelected 배열에 선택된 이미지의 이름을 저장한다. allSelected 상태로 전체 선택 여부를 관리한다.선택 삭제 버튼을 클릭하면 deleteFileMutation을 호출하여 isSelected을 전달하여 삭제를 처리한다.삭제 작업이 완료되면 searchImageQuery.refetch()를 호출하여 이미지 리스트를 최신 상태로 갱신한다.components/dropbox-images.tsx"use client"; export default function DropboxImage({ image, isSelected, setIsSelected, setAllSelected, totalLength, }) { const isChecked = isSelected.includes(image.name); const handleChecked = (checked) => { setIsSelected((prev) => { if (checked) { const newSelected = [...prev, image.name]; if (newSelected.length === totalLength) setAllSelected(true); return newSelected; } else { setAllSelected(false); return prev.filter((item) => item !== image.name); } }); }; // ... return ( <div className="relative w-full flex flex-col gap-2 p-4 border border-gray-100 rounded-2xl shadow-md"> {/* Image */} {/* fileName */} {/* update time */} {/* multiple checkBox */} <div className="absolute top-4 left-4"> <Checkbox color="blue" className="border-2 border-white bg-white/30 checked:border-white checked:bg-blue-500" checked={isChecked} onChange={(e) => handleChecked(e.target.checked)} /> </div> {/* trash Button */} </div> ); } 개별 체크박스: 각 이미지에 대해 체크박스를 제공하고 사용자가 선택한 이미지를 isSelected 배열에 추가하거나 제거한다.상태 변화: 체크박스를 클릭하면 해당 이미지가 선택되거나 선택이 해제되고 선택된 모든 이미지가 삭제될 때 "전체 선택" 체크박스도 자동으로 갱신된다. 상태 연동: isChecked 개별 이미지에 체크박스의 체크 여부를 결정한다.isSelected 배열에 현재 이미지의 name 값이 포함되어 있는지 true, false로 설정한다.개별 이미지의 체크 여부가 isSelected 상태와 동기화된다.actions/storageActions.tsexport async function deleteFile(fileName: string[]) { const supabase = await createServerSupabaseClient(); const { data, error } = await supabase.storage .from(process.env.NEXT_PUBLIC_STORAGE_BUCKET) .remove(fileName); handleError(error); return data; } supabase.storage.from(bucket).remove(['filename'])을 통해 지정한 버킷에서 배열에 포함된 모든 파일을 삭제 요청한다. 2주차 회고☀이번주는 강의를 들으며 여러 추가 기능이 생각나 실습하는 과정이 더욱 즐거웠다.다른 러너분이 한글 파일명 업로드 오류를 찾아내고 해결하는 모습을 보고 대단하다고 느꼈다.실습에서 영어 파일명으로만 업로드 했기 때문에 이런 오류가 발생하는지 몰랐다.상황을 공유해주신 러너분 덕분에 새로운 사실을 알게 됐다. (감사합니다!)다음에 시간이 난다면 강사님께서 말씀하신 해결 방법을 적용해 보고 싶다.목요일에 중간 점검 시간에 QnA 시간을 가졌는데, 정말 도움이 많이 됐다.특히, 포트폴리오 작성 요령과 개발자로서 필요한 역량을 채우는 방법을 핵심적으로 짚어주셔서 큰 도움이 되었다.나만의 특색을 찾고 포트폴리오에 잘 정리해 봐야겠다 느꼈다.벌써 2주차가 끝났는데, 배포까지 빠르게 진행해 보고 싶다. 다음주도 화이팅! 

치현

[인프런 워밍업 스터디 클럽 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기박우빈백엔드백엔드스터디지뢰찾기클린코드리팩토링

채널톡 아이콘