블로그
전체 132025. 06. 15.
0
인프런 워밍업 클럽 스터디 4기 - CS 전공지식 3주차 발자국
그림으로 쉽게 배우는 자료구조와 알고리즘 (심화편)만들면서 쉽게 배우는 컴퓨터 구조강의와 함께한 인프런 워밍업 클럽 스터디 4기 - CS 전공지식 (자료구조, 알고리즘, 컴퓨터구조)3주차 발자국 입니다.학습 내용 요약자료구조·알고리즘트라이(Trie)와 자동완성개념: 문자열 집합을 효율적으로 저장·탐색하기 위해, 각 노드가 문자 하나를 나타내며 공통 접두사를 공유하는 트리 구조입니다.학습: 삽입 및 탐색 로직의 흐름을 개념적으로 이해하였습니다.인사이트: 삽입·탐색이 문자열 길이에 비례하기 때문에, 대규모 단어 집합에서 접두사 검색이 빠르지만 메모리 사용이 커질 수 있음을 감안해야 함을 체감했습니다.그래프개념: 그래프는 정점과 간선으로 관계를 표현하는 자료구조로, 방향 여부나 가중치 유무에 따라 다르게 다루며 인접 리스트와 인접 행렬 방식으로 표현할 수 있습니다.학습: 간단한 예제 그래프를 구성해 보고, DFS는 재귀나 스택으로 깊이 우선 탐색하며 방문 체크로 순환을 막고, BFS는 큐로 레벨별 탐색해 비가중치 최단 경로에 유리함을 코드로 확인했습니다.인사이트: DFS는 깊이 탐색이 필요할 때, BFS는 단계별 탐색이 필요할 때 유용하며, 그래프 표현 방식도 데이터 크기와 목적에 맞춰 결정해야 한다는 점을 깨달았습니다.가중 그래프와 다익스트라 알고리즘개념: 가중치가 있는 그래프에서 시작 정점으로부터 다른 정점까지 최단 경로를 찾는 문제입니다.다익스트라 알고리즘원리: 현재까지 알려진 최단 거리 후보 정점을 선택해 인접 간선을 완화(relaxation)하며 거리 배열을 갱신합니다.인사이트: 그래프가 커질수록 일반 방식은 느려지고, 우선순위 큐(힙)를 쓰면 더 빠르게 최단 경로를 구할 수 있음을 느꼈습니다.컴퓨터 구조제어 장치 없는 컴퓨터 & 조립CPU 주요 구성 요소(CPU 레지스터, ALU, 프로그램 카운터, 스텝 카운터)를 복습하고, 실습을 통해 신호 흐름을 이해했습니다.CPU 만들기: 제어 장치(Control Unit)명령어 처리 흐름: 명령어 인출(fetch), 디코딩, 실행(execute) 단계의 제어 신호 타이밍을 확인했습니다.주요 명령어: NOP, LOADA, ADD, SUB, STOREA, LOADI, JMP/JMPC/JMPZ, OUT, HLT 등의 제어 신호와 ALU·레지스터·메모리 동작 방식을 이해했습니다. 제어 장치 조립: Logisim에서 각 단계별 신호를 조합해 제어 유닛을 완성하고, 시뮬레이션으로 신호 흐름과 분기 동작을 확인했습니다.미션🎯 자료구조·알고리즘 미션3: 허프만 코딩 구현목표주어진 문자열에 대해 HuffmanCoding.compress(str)를 호출했을 때, [ [문자, 코드], … ]형태의 배열을 출력하도록 허프만 코딩을 구현합니다.class Node { constructor(char = null, freq = 0, left = null, right = null) { this.char = char; this.freq = freq; this.left = left; this.right = right; } } class HuffmanCoding { compress(str) { if (!str) return []; // 빈도 계산 const freqMap = new Map(); for (const ch of str) { freqMap.set(ch, (freqMap.get(ch) || 0) + 1); } // 초기 노드 리스트 생성 const nodes = []; for (const [ch, f] of freqMap.entries()) { nodes.push(new Node(ch, f)); } // 허프만 트리 구성 while (nodes.length > 1) { nodes.sort((a, b) => a.freq - b.freq); const a = nodes.shift(); const b = nodes.shift(); nodes.push(new Node(null, a.freq + b.freq, a, b)); } const root = nodes[0]; // 코드 생성 const codes = {}; const build = (node, prefix) => { if (node.char !== null) { codes[node.char] = prefix || "0"; } else { build(node.left, prefix + "0"); build(node.right, prefix + "1"); } }; build(root, ""); // [문자, 코드] 쌍 배열 반환 return Object.entries(codes); } } const huffmanCoding = new HuffmanCoding(); const str = "Lorem ipsum dolor sit amet consectetur adipiscing elit. " + "Consectetur adipiscing elit quisque faucibus ex sapien vitae. " + "Ex sapien vitae pellentesque sem placerat in id. " + "Placerat in id cursus mi pretium tellus duis. " + "Pretium tellus duis convallis tempus leo eu aenean."; const result = huffmanCoding.compress(str); console.log(result);흐름 및 구현 요약빈도 계산문자열을 순회해 Map 또는 객체에 문자별 등장 빈도를 집계했습니다.코드 생성재귀 탐색으로 왼쪽 자식은 '0', 오른쪽 자식은 '1'을 prefix에 추가하며 리프에서 코드 할당. 단일 문자 입력 시 빈 prefix에 "0" 처리 로직도 반영했습니다.검증실행 결과에서 빈도가 높은 문자는 짧은 코드, 낮은 문자는 긴 코드가 할당되는지 확인했습니다.빈도와 코드 길이 관계가 적절한지, 모든 문자가 포함되었는지, 코드끼리 충돌이 없는지 점검했습니다. // 결과 [ [ 'i', '000' ], [ 'q', '001000' ], [ 'v', '001001' ], [ 'o', '00101' ], [ 'l', '0011' ], [ 'd', '01000' ], [ 'E', '0100100' ], [ 'g', '0100101' ], [ 'x', '0100110' ], [ 'P', '0100111' ], [ 'a', '0101' ], [ 'e', '011' ], [ 'm', '10000' ], [ 'r', '10001' ], [ 'u', '1001' ], [ 't', '1010' ], [ 'p', '10110' ], [ 'L', '10111000' ], [ 'C', '10111001' ], [ 'f', '10111010' ], [ 'b', '10111011' ], [ '.', '101111' ], [ ' ', '110' ], [ 's', '1110' ], [ 'c', '11110' ], [ 'n', '11111' ] ]느낀 점허프만 코딩의 "빈도 기반 병합 → 트리 구조 → 코드 생성" 흐름을 직접 구현하며 원리 습득이 명확해졌습니다. 결과 검증 과정에서 빈도와 코드 길이 관계, 프리픽스 성질 확인 등이 중요함을 체득했습니다. 🔗 자료구조·알고리즘 미션3 블로그 링크🎯 컴퓨터 구조 미션3: STOREB 명령어와 A·B 비교 어셈블리어 구현목표STOREB 명령어(OPcode 1001)를 추가해 레지스터 B의 값을 메모리 주소에 저장하도록 구현합니다.A와 B 값을 비교해 같으면 0, A>B면 1, A구현STOREB(1001) 명령어 추가기존 STOREA 처리 경로를 참고하여 opcode 1001 분기 로직을 제어 장치에 추가했습니다.스텝 카운터가 각 단계에 진입할 때, STOREB 신호와 연관된 핀들이 의도한 타이밍에 활성화되는지 확인했습니다.A·B 비교 어셈블리어메모리에 저장된 A와 B 값을 불러와 비교하는 순서LOADA addrA → SUB addrB (A = A - B, 플래그 설정)JMPC 분기로 A≥B 처리, 아닐 경우 LOADI 2로 A분기한 경우 ZF 검사: ZF=1일 때 A=B(LOADI 0), ZF=0일 때 A>B(LOADI 1)이후 OUT, HLT로 마무리주소 라벨 매핑과 분기 주소 계산을 꼼꼼히 검토했습니다.Logisim에서 다양한 A·B 조합 테스트를 통해 출력 레지스터가 기대값(0/1/2)을 잘 내보내는지 확인했습니다.LOADA 14 // A = RAM[14] SUB 15 // A -= RAM[15] JMPC 5 // CF = 1 → A ≥ B일 때 5번 주소로 점프 LOADI 2 // A B → 결과 1 JMP 9 // 결과 출력으로 점프 LOADI 0 // A = B → 결과 0 OUT // 결과 출력 HLT // 프로그램 종료 0 0 0 7 // A값 3 // B값느낀 점제어 장치에 새로운 명령어를 추가할 때, 기존 신호 경로와 타이밍 흐름을 정확히 이해해야 오류를 줄일 수 있음을 확인했습니다.분기 주소 계산이 미흡하면 예상치 못한 동작이 발생하므로, 어셈블리어 작성 시 라벨 관리가 중요함을 체감했습니다. 🔗 컴퓨터 구조 미션3 블로그 링크회고잘한 점단계적 접근: 컴퓨터 구조와 알고리즘 미션을 작은 단위로 나눠 핵심 로직과 흐름을 차근차근 구현하고 검증했습니다.디버깅 습관 강화: Logisim 시뮬레이션과 코드 실행 결과를 반복 확인하며, 문제 발생 시 신호 흐름이나 분기 주소 동작을 빠르게 점검했습니다.이론·실습 병행: 트라이, 그래프, 다익스트라 개념 학습 뒤 직접 구현 연습, 제어 유닛 명령어 설계 뒤 Logisim 실습, 허프만 코딩 이론 뒤 코드 작성으로 이론과 실습을 잘 연결했습니다. 아쉬웠던 점 & 보완테스트 부족 : 테스트가 부족해 다양한 상황을 확인하지 못했습니다. 다음에는 다양한 입력과 경계값을 더 많이 시도해 보겠습니다.성능 최적화 부족: 성능 최적화 연습이 부족했습니다. 시간이 된다면 구현했던 자료구조와 알고리즘을 재작성해보고, 다양한 시도를 통해 실행 시간을 비교해 보겠습니다. 마치며이번 주차에는 Trie, 그래프, 다익스트라 학습을 진행하고, 제어 장치 명령어 확장과 허프만 코딩 구현 미션을 병행하며 이론과 실습을 모두 경험했습니다. 단계별 실습을 통해 개념이 실제 코드나 회로로 연결되는 과정을 체감했고, 디버깅과 테스트 습관을 강화할 수 있었습니다. 다음주에는 추가 알고리즘 학습을 통해 더욱 탄탄한 이해를 쌓아가겠습니다. 감사합니다!
알고리즘 · 자료구조
・
인프런
・
워밍업클럽
・
스터디
・
CS
・
전공지식
・
자료구조
・
컴퓨터구조
・
발자국
・
회고
・
4기
2025. 06. 08.
0
인프런 워밍업 클럽 스터디 4기 - CS 전공지식 2주차 발자국
그림으로 쉽게 배우는 자료구조와 알고리즘 (심화편)만들면서 쉽게 배우는 컴퓨터 구조강의와 함께한 인프런 워밍업 클럽 스터디 4기 - CS 전공지식 (자료구조, 알고리즘, 컴퓨터구조)2주차 발자국 입니다.학습 내용 요약그림으로 쉽게 배우는 자료구조와 알고리즘 (심화편)이번 주에는 균형 이진 탐색 트리의 대표 주자인 Red-Black 트리의 삽입·삭제 개념과 실제 구현을 다뤘습니다.개념(삽입/제거) 단계에서 색상 규칙과 회전을 통해 균형을 유지하는 원리를 학습했고보조 함수(색 변경, 회전 함수), 삽입 로직, 삭제 로직을 차례로 완성하며, 각 단계마다 균형 인자가 올바르게 갱신되는지 확인했습니다.이어 우선순위 큐와 힙을 학습했습니다.힙(Heap) 의 개념과 완전 이진 트리 구조를 이해하고, 힙 삽입, 힙 제거 과정을 직접 구현해 보았습니다.이를 기반으로 미션에서 CpuScheduler 를 만들어, 실행 횟수 우선 & I/O 바운드 우선 정책을 Heap 비교 함수를 수정해 적용했으며마지막으로 힙 정렬(Heapsort) 알고리즘까지 학습했습니다.만들면서 쉽게 배우는 컴퓨터 구조지난주 불 대수·진리표·Logisim 기초를 다진 뒤, 이번 주에는MUX: 1비트 2입력부터 8비트 16입력까지 다양한 MUX 설계디코더: 3비트·4비트 디코더 설계컨트롤 버퍼(Buffer) 이해ALU: 반 가산기·전 가산기·ALU 제작메모리: SR/D/JK 래치, 플립플롭, 레지스터, RAM 구현까지 확장하며, 각 모듈을 직접 구현하고 테스트하며 신호 흐름을 검증했습니다.미션🎯 미션1. 자료구조와 알고리즘 미션: CPU 스케줄링목표프로세스의 executionCount(실행 횟수)와 cpuReturnCount(I/O 바운드 횟수)로 우선순위를 매기는 스케줄러 구현실행 횟수(executionCount)가 가장 작은 프로세스가 우선순위가 높다.실행 횟수가 같을 경우, I/O Bound 프로세스(cpuReturnCount가 큰 프로세스) 가 우선순위가 높다.구현 개요자료구조: 완전 이진 트리 기반의 Heap 활용비교 함수 재정의// CpuScheduler 생성자 내부 this.heap.isBigPriority = (first, second) => { if (first.executionCount !== second.executionCount) { // 실행 횟수가 적은 프로세스가 우선 return first.executionCount second.cpuReturnCount; }; CpuScheduler 클래스import { Heap } from "../../heap/heap.mjs"; class Process { constructor(name, cpuReturnCount, executionCount) { this.name = name; this.cpuReturnCount = cpuReturnCount; this.executionCount = executionCount; } } class CpuScheduler { constructor() { this.heap = new Heap(); this.heap.isBigPriority = (first, second) => { if (first.executionCount !== second.executionCount) { return first.executionCount second.cpuReturnCount; }; } enqueue(process) { this.heap.insert(process); } dequeue() { const node = this.heap.remove(); return node ? node.getData() : null; } } let cpuScheduler = new CpuScheduler(); cpuScheduler.enqueue(new Process("수치계산프로그램", 4, 0)); // cpu반납 4회, 실행횟수 0회 cpuScheduler.enqueue(new Process("뮤직플레이어", 11, 10)); // cpu반납 11회, 실행횟수 10회 cpuScheduler.enqueue(new Process("웹브라우저", 27, 25)); // cpu반납 27회, 실행횟수 25 cpuScheduler.enqueue(new Process("터미널1", 34, 2)); // cpu반납 34회, 실행횟수 2회 cpuScheduler.enqueue(new Process("터미널2", 93, 2)); // cpu반납 93회, 실행횟수 2회 console.log(cpuScheduler.dequeue()); // 수치계산프로그램 프로세스 출력 console.log(cpuScheduler.dequeue()); // 터미널2 프로세스 출력 console.log(cpuScheduler.dequeue()); // 터미널1 프로세스 출력 console.log(cpuScheduler.dequeue()); // 뮤직플레이어 프로세스 출력 console.log(cpuScheduler.dequeue()); // 웹브라우저 프로세스 출력테스트 결과Process { name: '수치계산프로그램', cpuReturnCount: 4, executionCount: 0 } Process { name: '터미널2', cpuReturnCount: 93, executionCount: 2 } Process { name: '터미널1', cpuReturnCount: 34, executionCount: 2 } Process { name: '뮤직플레이어', cpuReturnCount: 11, executionCount: 10 } Process { name: '웹브라우저', cpuReturnCount: 27, executionCount: 25 }O(log N) 삽입·삭제 성능으로 대규모 프로세스 관리 가능비교 함수만 바꿔 다양한 스케줄링 정책 가능🔗 자료구조·알고리즘 미션2 블로그 링크🎯 미션2. 컴퓨터 구조 미션: 터널 연결부터 32바이트 RAM까지미션 구성모든 회로 연결을 터널(Tunnel)로 대체8비트 32입력 MUX 제작10비트 입력 두 개(A, B)를 계산하는 ALU 설계32바이트 RAM 구현1) 모든 회로 연결을 터널(Tunnel)로 대체목표: Logisim의 Tunnel 컴포넌트를 사용해 복잡한 선 숨기기결과: 캔버스가 한눈에 들어오고, 라벨만으로 신호 흐름 파악 가능2) 8비트 32입력 MUX 제작목표: IN_0…IN_31 중 하나 선택하여 출력구현: Logisim 내장 32-to-1 MUX + 8비트 버스검증: SELECTION 값 변화 시 기대 출력 확인3) 10비트 입력 두 개(A, B)를 계산하는 ALU 설계목표: 덧셈(SU=0)/뺄셈(SU=1), Carry-in/Carry-out, Zero Flag구현:10개 Full Adder 체인B 입력 XOR + SU → 뺄셈 지원마지막 Adder의 Carry-out → CF, 출력 전 비트 OR·NOT → ZF검증: 다양한 A, B 조합 테스트, CF/ZF 정상 동작4) 32바이트 RAM 구현목표: 5비트 주소 + 8비트 데이터 입·출력, ENABLE_WRITE, ENABLE_OUT구현:5→32 디코더 + AND → 각 레지스터 Load읽기 MUX로 선택 출력검증: WRITE=1 시 해당 워드에 저장, OUT=1 시 올바른 데이터 읽기 🔗 컴퓨터 구조 미션2 블로그 링크회고잘한 점단계적 학습Red-Black 트리 개념 → 보조 함수 → 삽입 → 삭제까지 차근차근 구현하며 균형 유지 원리를 확실히 체득했습니다.힙 기반 스케줄러와 힙 정렬, 다양한 크기의 MUX·디코더 구현으로 자료구조·회로 설계 감각을 크게 확장했어요.모듈화 & 재사용성Heap 비교 함수만 바꿔 다양한 스케줄링 정책을 손쉽게 실험했고, Logisim 터널·버퍼 적용으로 회로 블록을 깔끔하게 재사용할 수 있었습니다.플래그·메모리 회로 반복 학습반·전 가산기, Zero/Carry Flag, 래치·플립플롭, 레지스터 파일, RAM까지 차례로 구현하며 신호 흐름과 제어 신호 이해가 깊어졌습니다.아쉬웠던 점 & 보완테스트 케이스 부족Red-Black 트리 삭제의 예외 케이스를 더 다양하게 검증하지 못했습니다.힙 스케줄러에도 executionCount·cpuReturnCount 동률 시나리오를 추가할 필요가 있습니다.시간 관리다양한 모듈 구현에 몰두하느라 후반부 학습이 빠듯했습니다. 마치며이번 주차에도 "작게 쪼개고 하나씩 검증하는" 학습 방식을 통해, 알고리즘과 회로 설계 모두에서 자신감을 많이 얻었습니다.다음 주에는 부족했던 것들을 보완해, 더 탄탄한 결과물을 만들어 보겠습니다. 감사합니다!
알고리즘 · 자료구조
・
인프런
・
워밍업클럽
・
스터디
・
CS
・
전공지식
・
자료구조
・
컴퓨터구조
・
발자국
・
회고
・
4기
2025. 06. 01.
1
인프런 워밍업 클럽 스터디 4기 - CS 전공지식 1주차 발자국
그림으로 쉽게 배우는 자료구조와 알고리즘 (심화편)만들면서 쉽게 배우는 컴퓨터 구조강의와 함께한 인프런 워밍업 클럽 스터디 4기 - CS 전공지식 (자료구조, 알고리즘, 컴퓨터구조)1주차 발자국 입니다.학습 내용 요약이번 주에는 두 가지 강의를 통해 자료구조·알고리즘과 컴퓨터 구조의 큰 틀을 살펴보며, 이론과 실습을 병행했습니다.그림으로 쉽게 배우는 자료구조와 알고리즘 (심화편)크게 트리 구조와 이진 탐색 트리(BST), 그리고 그 위에 균형을 더한 AVL 트리를 다뤘습니다.초반에는 "트리와 이진 트리 개념"으로 부모·자식 노드 간 관계를 복습했고, 이어서 BST의 삽입·검색·삭제 과정을 훑어보았습니다.마지막으로 AVL 트리 개념과 구현(보조 함수, 삽입, 삭제)을 익히며, "왜 균형 인자가 중요한지"와 "삽입/삭제 후 어떻게 회전으로 높이를 조정하는지"를 이해했습니다.만들면서 쉽게 배우는 컴퓨터 구조컴퓨터 구조 전반(블랙박스 관점, CPU·메모리·주변 장치)과 함께, 불 대수 및 논리 게이트 설계로 넘어가 간단한 하드웨어 시뮬레이션을 해보았습니다.먼저 "컴퓨터 구조를 배우는 이유"와 명령어가 메모리→CPU→다시 메모리로 흐르는 과정을 개념적으로 파악했고, CPU 내부와 메모리 계층도 간단히 살펴봤습니다.이어서 불 대수 기본 연산과 성질을 학습한 뒤, 직접 Logisim을 설치해 NAND/NOT/AND/OR/XOR 게이트를 조합해 보는 실습을 경험했습니다.또 10진수↔2진수 변환, 16진법, 빅·리틀 엔디안, 2의 보수 음수 표현 같은 비트 단위 개념을 짚으며, 디지털 회로의 밑바탕이 되는 이진 표현 방식을 정리했습니다.미션🎯 자료구조·알고리즘 미션1: GarbageCollector 클래스 구현목표: AVL 트리로 빈 메모리 블록을 관리해, 요청 크기와 정확히 일치하는 블록이나 그보다 큰 값 중 최소값을 찾아 반환·삭제하도록 한다.코드import { AVLTree } from "../../avlTree/avlTree.mjs"; class GabageCollector { constructor() { this.avlTree = new AVLTree(); } // 빈 메모리 블록 삽입 insertFreeMemory(size) { this.avlTree.root = this.avlTree.insert(this.avlTree.root, size); } // 요청 크기 이상인 블록 검색 searchFreeMemory(requestSize) { let current = this.avlTree.root; let candidate = null; while (current) { const nodeSize = current.getData(); if (nodeSize === requestSize) { // 정확히 같은 크기를 찾으면 즉시 반환 candidate = current; break; } if (nodeSize > requestSize) { // 후보로 저장한 뒤, 더 작은 후보를 찾기 위해 왼쪽 서브트리 탐색 candidate = current; current = current.getLeftSubTree(); } else { current = current.getRightSubTree(); } } return candidate; } // 반환된 블록(크기) 삭제 releaseFreeMemory(size) { this.avlTree.root = this.avlTree.remove(this.avlTree.root, size); } } // 실행 const gc = new GabageCollector(); console.log("========== 빈 메모리 영역 초기화 =========="); gc.insertFreeMemory(64); // 빈 64바이트 삽입 gc.insertFreeMemory(48); // 빈 48바이트 삽입 gc.insertFreeMemory(87); // 빈 87바이트 삽입 gc.insertFreeMemory(13); // 빈 13바이트 삽입 gc.insertFreeMemory(102); // 빈 102바이트 삽입 gc.insertFreeMemory(34); // 빈 34바이트 삽입 gc.insertFreeMemory(61); // 빈 61바이트 삽입 gc.insertFreeMemory(40); // 빈 40바이트 삽입 gc.insertFreeMemory(6); // 빈 6바이트 삽입 let freeMemory1 = gc.searchFreeMemory(64); // 64바이트 메모리 console.log(freeMemory1); if (freeMemory1) { gc.releaseFreeMemory(freeMemory1.data); } let freeMemory2 = gc.searchFreeMemory(42); // 48바이트 메모리 획득 console.log(freeMemory2); if (freeMemory2) { gc.releaseFreeMemory(freeMemory2.data); }실행 요약const gc = new GarbageCollector(); 빈 블록 [64, 48, 87, 13, 102, 34, 61, 40, 6]을 insertFreeMemory(...)로 순차 삽입 searchFreeMemory(64) → 정확히 64 노드를 찾아 반환 → releaseFreeMemory(64) 삭제 searchFreeMemory(42) → 42와 동일한 값이 없어 “42보다 큰 값 중 최소(48)” 반환 → releaseFreeMemory(48) 삭제 회고AVL 트리의 삽입·삭제 시 "LL·RR·LR·RL 회전"이 실제로 동작하는 것을 확인하며, 균형 인자의 중요성을 체감했습니다. 삽입, 삭제, 검색이 모두 로그 시간에 동작하기 때문에, 빈 메모리 블록이 많아질 때도 성능을 보장할 수 있다는 점을 체감했습니다. "정확히 같은 크기가 없을 때, 그보다 큰 값 중 최소값을 찾는 후보(candidate)" 로직을 루트부터 내려가며 올바르게 업데이트해야 합니다. 🔗자료구조·알고리즘 미션1 블로그 링크🎯 컴퓨터 구조 미션1 (총 5개)미션 1. 4입력 논리 연산 진리표 작성내용: 4개의 입력(A, B, C, D)에 대한 AND, OR, NAND, NOR, XOR 진리표를 총 16조합으로 작성실행A B C D를 0000 → 0001 → … → 1111 순서로 나열 각 연산(AND, OR, NAND, NOR, XOR)의 출력(Q)을 직접 채워 넣음느낀 점2진수 순서대로 입력 조합을 정리하니 빠짐없이 작성할 수 있었고, XOR처럼 "1 개수 홀짝 판정" 연산은 중간에 헷갈려도 정리 순서를 엄수하면 실수를 줄일 수 있었다.미션 2. 불 방정식 간략화내용: 아래 네 개의 불 방정식을 단계별로 법칙(아이디엠포턴트, 드모르간, 흡수 등)을 적용해 간략화했다.A( (BB)’ + 0A) + (CC)’ = (AB’) + C’ (B’B’) + (AD’ + (CA)’)D = B’ + (DC’) + (DA’) (A’B) + B(B1 + BC) = B B’(1C + BD) + DB = (B’C) + (DB) 느낀 점드모르간·흡수법칙을 차례로 적용하며 "복잡해 보이던 식이 단번에 간결해지는 과정"이 인상적이었다.단계별로 어떤 법칙을 먼저 적용해야 가장 효율적으로 축약되는지 아직 직관이 부족해, 다음주엔 다양한 예제를 풀어 보며 연습할 예정이다.미션 3. 2진수를 10진수로 변환내용: 다음 네 개의 2진수를 10진수로 바꿔 보았다.110111₂ = 55₁₀ 10000001₂ = 129₁₀ 11111100000₂ = 2016₁₀ 101010₂ = 42₁₀실행 각 2진수를 우측 끝 비트부터 자리값(2⁰, 2¹, 2², …)을 곱해 합산 예: 11111100000₂ = 1·2¹⁰ + 1·2⁹ + … + 1·2⁵ + 0·… = 1024 + 512 + 256 + 128 + 64 + 32 = 2016느낀 점자릿값을 일일이 계산하는 것은 번거롭지만, "가장 큰 2의 거듭제곱부터 차례로 빼 나가는 방식"으로 익히니 긴 이진수도 비교적 빠르게 변환할 수 있었다.미션 4. 10진수를 2진수로 변환내용: 다음 네 개의 10진수를 2진수로 바꿔 보았다.10₁₀ = 1010₂ 27₁₀ = 11011₂ 86₁₀ = 1010110₂ 516₁₀ = 1000000100₂실행 나눗셈→나머지 반복: 예를 들어 86 ÷ 2 = 43, 나머지 0 → 43 ÷ 2 = 21, 나머지 1 → … → 최종 이진수 역순 또는 "가장 큰 2의 거듭제곱(2⁶=64)부터 빼 나가기" 방식으로 빠르게 도출느낀 점처음엔 헷갈렸지만, "2ⁿ 이하 최대값을 찾아서 빼고, 나머지로 또 반복"하는 방식이 머릿속에 각인되자 더 긴 수도 금방 변환할 수 있었다.미션 5. 불 방정식을 Logisim으로 회로 구현내용: 불 방정식을 logisim을 이용해 회로 만들기 실행(B′·C) + (D·B)B → NOT → B′AND1: (B′, C)AND2: (D, B)OR: (AND1, AND2) → 출력(A·B′) + CB → NOT → B′AND: (A, B′)OR: (AND, C) → 출력B′ + (D·C′) + (D·A′)B → NOT → B′ (첫 OR 입력)C → NOT → C′, A → NOT → A′AND1: (D, C′)AND2: (D, A′)OR( B′, AND1, AND2 ) → 출력대표 입력별 검증(B′·C)+(D·B) → B=0,C=1,D=0 → OUT=1, B=1,C=0,D=1 → OUT=1, B=1,C=1,D=0 → OUT=0(A·B′)+C → A=0,B=0,C=1 → OUT=1, A=1,B=1,C=0 → OUT=0B′+(D·C′)+(D·A′) → A=0,B=0,C=0,D=0 → OUT=1, A=1,B=1,C=0,D=1 → OUT=1느낀 점Logisim을 통해 "진리표상의 연산이 실제 게이트 단위 회로에서 어떻게 동작하는지"를 직접 눈으로 확인할 수 있었다.회로가 복잡해질수록 배선이 겹치는 문제가 있었으나, 다음에는 모듈 단위(예: (B′·C) 회로 → (D·B) 회로 → OR)로 나눠서 단계별로 검증하려 한다. 🔗컴퓨터 구조 미션1 블로그 링크회고이번 주에는 AVL 트리와 논리 회로 설계를 함께 학습했습니다.AVL 트리 구현: 삽입·삭제 시 LL·RR·LR·RL 회전을 거쳐 균형을 유지하는 과정을 직접 코드로 확인했습니다. "정확히 같은 값이 없으면 그보다 큰 값 중 최소값을 찾는 로직"을 구현하면서, 균형 인자를 어떻게 업데이트해야 하는지 확실히 알게 되었습니다.논리 회로 설계: 4입력 진리표 작성부터 불 방정식 간략화, Logisim 회로 구성까지 순서대로 진행했습니다. 직접 회로를 만들어 입력을 바꿔 볼 때마다 결과가 즉시 바뀌는 모습을 보면서, 이론이 실제 게이트 동작으로 이어진다는 점이 와닿았습니다.스스로 칭찬하는 점이론과 실습 병행AVL 트리 코드와 Logisim 회로를 동시에 구현하며, 배운 내용을 손으로 직접 확인한 점이 좋았습니다.진리표 정리법 습득2진수 순서대로 진리표를 빠짐없이 작성하면서, 체계적인 정리 방법을 제대로 익혔습니다.아쉬웠던 점 & 보완회로 배선 정리 부족Logisim에서 선이 겹치다 보니 가끔 헷갈렸습니다.→ 다음에는 작은 모듈로 나눠 단계별로 검증하고, 마지막에 합치는 방식을 사용하겠습니다. 개념 정리 자료 부족AVL 트리나 불 대수 법칙을 공부할 때, 머릿속으로만 이해하고 따로 요약해 두지 않아 복습할 때 헷갈리는 부분이 있었습니다.→ 학습 중에는 핵심 개념을 노트에 간단히 정리하여, 부족한 부분을 바로 찾아볼 수 있도록 하겠습니다.마치며이번 주차는 이론을 코드와 회로로 연결해 보는 경험을 했습니다. 다음 주에도 부족했던 부분을 보완하며 차근차근 학습을 이어가겠습니다! 감사합니다!
알고리즘 · 자료구조
・
인프런
・
워밍업클럽
・
스터디
・
CS
・
전공지식
・
자료구조
・
컴퓨터구조
・
발자국
・
회고
・
4기
2025. 04. 09.
1
인프런 워밍업 클럽 스터디 3기 - 백엔드 클린 코드, 테스트 코드 후기
Readable Code: 읽기 좋은 코드를 작성하는 사고법Practical Testing: 실용적인 테스트 가이드강의와 함께한 인프런 워밍업 클럽 스터디 3기 - 백엔드 클린 코드, 테스트 코드 (Java, Spring Boot) 후기 입니다.소개이번 스터디 3기는 백엔드 클린 코드, 테스트 코드를 주제로 진행하였습니다. “Readable Code: 읽기 좋은 코드를 작성하는 사고법”과 “Practical Testing: 실용적인 테스트 가이드” 강의를 함께 학습함으로써, 더 나은 코드 가독성과 안정적인 테스트를 위한 다양한 방법론을 실습할 수 있었습니다.지난 2기 때 Kotlin과 Spring Boot로 백엔드 개발 전반을 경험하며 웹 애플리케이션 동작 원리를 학습했다면, 이번 3기에서는 코드 품질과 테스트라는 핵심 주제를 깊이 파고들어, 보다 깨끗하고 유지보수하기 좋은 코드, 그리고 신뢰도 높은 테스트 코드를 작성하는 법을 익혔습니다.아래에서는 각 주차별 학습 내용을 정리하고, 전체 스터디를 통해 느낀 점과 앞으로의 계획을 공유하고자 합니다. 1주차: 클린 코드를 위한 기초 - 추상과 구체, SOLID 원칙1주차 주요 학습 내용추상과 구체의 개념논리와 사고의 흐름SOLID 원칙 (SRP, OCP, LSP, ISP, DIP)리팩토링 미션 수행1주차에서는 코드 가독성을 높이는 가장 기본이 되는 개념들을 배웠습니다. 특히 추상과 구체의 균형을 어떻게 잡아야 코드가 명확해지고, 협업자가 빠르게 이해할 수 있는지 고민할 수 있었습니다.추상은 핵심 개념과 의도만 드러내도록 하는 장점이 있지만, 너무 과도하면 실제 구현과 괴리가 생겨 혼란을 줄 수 있다는 점을 배웠습니다.구체는 세세한 내용을 모두 표현해 직관적이지만, 과도해지면 복잡해지고 가독성이 떨어질 수 있으므로 주의가 필요했습니다.또한 Early Return이나 부정어 최소화 같은 리팩토링 기법을 적용해 보면서, 간단한 습관만으로도 코드의 가독성이 크게 높아진다는 점을 실감했습니다. 2주차: 코드 구조 및 리팩토링 - 패키지 구조, 주석, 적정 기술2주차 주요 학습 내용효과적인 주석 사용 방식변수와 메서드의 나열 순서패키지 구조 설계와 적정 기술(Over Engineering 지양)스터디카페 이용권 시스템 리팩토링2주차에서는 코드 구조 전반을 깔끔하게 정비하는 방법을 중심으로 학습했습니다.주석은 변동이 잦거나 오해를 일으킬 만한 내용을 담지 말고, 정말 필요할 때만 사용해야 한다는 것을 다시금 확인했습니다.변수와 메서드를 사용하는 순서대로 배치하고, 공개 메서드(public) → 비공개 메서드(private) 순서로 자연스럽게 구성함으로써, 읽는 흐름을 방해하지 않는 것이 중요하다는 점을 배웠습니다.특히 리팩토링 미션을 통해 패키지 구조가 지나치게 단순하거나, 반대로 너무 세분화되어 관리가 어렵지 않은지 돌아볼 수 있었습니다. 적정 기술을 사용하는 태도. 즉, 문제를 해결하는 데 필요한 만큼만 기술 스택과 디자인 패턴을 적용하는 것이 얼마나 중요한지도 체감하게 되었습니다. 3주차: Spring & JPA, 계층형 아키텍처, 통합 테스트3주차 주요 학습 내용Spring & JPA를 이용한 계층형 아키텍처 (Controller, Service, Repository)단위 테스트와 통합 테스트의 차이점 및 작성 방법MockMvc, @DataJpaTest 등을 활용한 테스트 실습Bean Validation과 ExceptionHandler로 유효성 검증 및 예외 처리3주차부터는 실무에서 흔히 쓰이는 Spring & JPA 기반의 계층형 아키텍처에 초점을 맞춰, 전체적인 흐름을 학습했습니다.Layered ArchitecturePresentation Layer(Controller)Business Layer(Service)Persistence Layer(Repository)이러한 구조를 바탕으로, MockMvc를 사용해 Controller부터 Service, Repository까지 이어지는 통합 테스트를 작성해 봄으로써, 애플리케이션 전반이 정상 동작하는지 확인할 수 있었습니다.또한 @NotNull, @Positive 등 Bean Validation 애너테이션과 @RestControllerAdvice를 통한 예외 처리 방식도 연습해보았습니다. 4주차: Mock, Spy, @MockBean / @SpyBean, 레이어별 테스트 전략4주차 주요 학습 내용Mockito에서의 @Mock, @Spy, @InjectMocks스프링 환경에서의 @MockBean, @SpyBean레이어별 테스트 설계: 단위 테스트 vs 통합 테스트테스트 시나리오 설계(given-when-then)와 @BeforeEach 활용마지막 주차에서는 테스트 유지보수와 테스트 코드 설계의 중요성을 배웠습니다.@Mock, @Spy를 통해 순수 단위 테스트 환경에서 외부 의존성을 제거(또는 부분 제거)하여 빠른 피드백을 받을 수 있었습니다.반면 @MockBean, @SpyBean은 스프링 애플리케이션 컨텍스트를 띄운 상태에서 기존 Bean을 모의 객체로 교체할 수 있어, 좀 더 넓은 범위의 통합 테스트에 활용할 수 있었습니다.레이어별 테스트 (Controller, Service, Repository)를 분리해 진행하면 테스트가 더욱 명확해지고, 문제 발생 시 어느 레이어가 원인인지 빠르게 파악할 수 있음을 깨달았습니다.가장 크게 느낀 점은 ‘테스트도 코드의 일부’라는 사실이었습니다. 테스트 코드를 지속적으로 관리하고 개선해야, 유지보수가 필요한 시점에 믿을 수 있는 안전망이 되어준다는 것을 명확히 알게 되었습니다.회고이번 스터디 3기를 통해 "클린 코드와 테스트"라는 주제의 중요성을 다시 한번 실감했습니다. 단순히 기능만 구현하는 것이 아닌, 가독성, 유지보수성, 테스트 커버리지까지 고려해야 진정한 의미의 소프트웨어 품질을 높일 수 있음을 배웠습니다.아쉬웠던 점 & 보완 계획실프로젝트 적용: 강의와 미션 중심의 학습이기에, 실제 현업 수준의 복잡도를 가진 코드에 적용할 기회가 많지 않았습니다. 앞으로 개인/팀 프로젝트에서 적극 활용해 경험치를 높이고자 합니다.테스트 시나리오 다양성: 해피 케이스 중심의 테스트가 많았기에, 예외 상황과 경계값 테스트를 더 폭넓게 다루면 좋았을 것 같습니다. 마무리인프런 워밍업 클럽 스터디 3기를 통해, 클린 코드와 테스트 코드 작성 역량을 크게 향상시킬 수 있었습니다.코드: 명료한 이름 짓기, 중복 제거, 적절한 추상화 등을 통해 읽기 좋고 변화에 강한 코드를 추구하게 되었습니다.테스트: 프로젝트가 확장되더라도, 안정성을 확보할 수 있는 테스트 설계가 얼마나 중요한지 다시금 느끼게 되었습니다. 이번 스터디에서 성실히 참여한 결과, 2기에 이어 3기에서도 우수러너로 선정되는 영광도 누릴 수 있었습니다. 앞으로도 스스로 레거시 코드를 리팩토링하거나, 프로젝트를 확장하면서 배운 내용을 꾸준히 적용할 것입니다. 스터디에서 함께한 분들, 그리고 인프런과 워밍업 클럽 관계자분들께 감사드리며, 지속적인 학습과 공유를 통해 더 나은 개발자가 되도록 노력하겠습니다. 감사합니다!
백엔드
・
인프런
・
인프런워밍업클럽
・
스터디3기
・
워밍업
・
백엔드
・
후기
・
클린
・
테스트
・
코드
2025. 03. 30.
0
인프런 워밍업 클럽 스터디 3기 - 백엔드 클린 코드, 테스트 코드 4주차 발자국
Readable Code: 읽기 좋은 코드를 작성하는 사고법Practical Testing: 실용적인 테스트 가이드강의와 함께한 인프런 워밍업 클럽 스터디 3기 - 백엔드 클린 코드, 테스트 코드 (Java, Spring Boot)4주차 발자국 입니다.학습 내용 요약이번 주에는 Practical Testing: 실용적인 테스트 가이드 강의에서 계층형 아키텍처(Layered Architecture) 의 개념을 다시 한번 정리하고, 스프링 기반 테스트에서 자주 사용하는 Mock/Spy 기법과 관련 애너테이션들을 배웠습니다.Layered Architecture (계층형 아키텍처)Presentation Layer: 사용자 입력과 응답을 담당. API 요청/응답 혹은 프론트엔드 인터페이스를 제공하며, 기본적인 파라미터 검증과 변환만 수행.Business Logic Layer: 애플리케이션의 핵심 비즈니스 로직과 규칙을 구현. 서비스(Service)나 도메인(Domain) 로직이 위치하며, 트랜잭션 관리의 주된 대상이 됨.Persistence Layer: 데이터베이스/파일시스템 등 영속성 저장소와 직접적으로 상호작용. Repository(DAO) 형태로 CRUD를 담당.Mockito & Spring Test 관련 애너테이션@Mock / @Spy / @InjectMocks: 순수 Mockito 환경(스프링 컨텍스트 없이)에서 가짜 객체(또는 부분 가짜 객체)를 만들어 단위 테스트를 쉽게 작성할 수 있게 함.@MockBean / @SpyBean: 스프링 애플리케이션 컨텍스트를 기동하는 통합 테스트 환경에서 특정 Bean을 Mock 또는 Spy로 교체하여, 외부 의존성을 간단히 제어하고자 할 때 활용.테스트 설계 방향각 레이어별로 테스트 범위를 명확히 하여, “Persistence → Business → Presentation” 순으로 책임을 분리해 점진적으로 테스트 커버리지를 넓힘.@MockBean / @SpyBean을 과도하게 사용하지 않도록 주의: 꼭 필요한 부분만 모의(Mock/Spy) 처리하고, 나머지는 실제 로직을 이용해 통합 테스트를 안정적으로 수행.테스트 시나리오별로 @BeforeEach를 활용하는 방법과, 각 테스트 메서드에서 독립적으로 given-when-then을 구성하는 방법을 비교.학습 회고얻은 인사이트레이어별 책임과 역할이 분명해야 복잡한 서비스에서도 테스트 설계가 한결 명확해짐을 느꼈습니다.Mock/Spy를 올바른 상황에서만 사용하면 외부 의존성을 최소화하여 빠른 단위 테스트를 작성할 수 있으나, 반대로 불필요하게 남발하면 테스트 유지보수가 어려워짐을 체감했습니다.어려웠던 점@MockBean과 @SpyBean을 사용해 스프링 컨텍스트를 띄울 때 테스트 속도가 느려지거나, 동일 타입 Bean이 여러 개일 때 어느 Bean을 대체하는지 혼동될 수 있었습니다.레이어별 테스트를 작성하면서, 중복으로 보이는 코드(예: 테스트 준비 로직)를 어느 정도까지 @BeforeEach로 추출해야 할지 고민이 되었습니다.미션🎯 Day16 미션: Layered Architecture 구조의 레이어별 특징과 테스트 방법요구사항Layered Architecture에서 각 레이어가 하는 역할, 특징, 그리고 테스트 방식을 자기만의 언어로 정리하기나만의 정리Presentation Layer역할: 사용자나 외부 시스템의 요청을 받고 응답을 전달. 파라미터 검증, DTO 변환, HTTP 상태 코드 결정 등을 담당.특징: 비즈니스 로직을 직접 수행하지 않고, 유효성 체크 후 Service 호출 → 결과 반환에 집중.테스트 방법: 컨트롤러 통합 테스트(@SpringBootTest, @WebMvcTest + MockMvc)를 통해 실제 요청/응답 형식을 모의하거나, 프론트엔드와의 e2e 테스트를 진행해볼 수도 있음.Business Logic Layer역할: 애플리케이션의 핵심 규칙, 알고리즘, 트랜잭션 등의 로직 담당.특징: 여러 Repository를 조합하여 도메인 로직을 수행하고, 예외 처리, Validation 등의 업무 로직을 포괄적으로 관리.테스트 방법: 단위 테스트를 통해 특정 비즈니스 로직의 정확성을 검증. 필요 시 Repository를 Mock 처리(예: @Mock, @MockBean)하여 DB 의존성을 제거하고 로직만 집중 테스트.Persistence Layer역할: 데이터의 저장/조회/수정/삭제 등 영속성 처리에 집중.특징: 쿼리 작성, DB 연결, CRUD 로직을 추상화한 Repository/DAO 형태로 제공.테스트 방법: 실제 DB 혹은 In-Memory DB(H2 등)를 활용한 통합 테스트(@DataJpaTest). 쿼리 정확도, 트랜잭션 처리, 성능 등을 검증하기에 좋음.🎯 Day18 미션 1: @Mock, @MockBean, @Spy, @SpyBean, @InjectMocks의 차이점 정리요구사항각 애너테이션이 어떤 환경(단위 테스트/통합 테스트)에서 사용되고, 어떤 특징이 있는지 정리하기1) @Mock주로 사용 환경: Mockito (단위 테스트)스프링 빈 등록 여부: X (별도 Bean 아님)특징:순수 Mock 객체를 생성하여, 내부 로직 없이 호출 기록/결과(Stubbing)만 지정할 수 있음단위 테스트에서 외부 의존성을 배제하고 특정 로직만 검증할 때 유용2) @MockBean주로 사용 환경: Spring Boot Test(통합 테스트)스프링 빈 등록 여부: O (빈으로 등록됨)특징:스프링 애플리케이션 컨텍스트에 등록된 실제 Bean을 Mock으로 교체다른 Bean이 해당 Bean을 의존하고 있으면, 그 의존성도 가짜(Mock)로 처리됨@SpringBootTest나 @WebMvcTest 등 스프링 컨텍스트가 뜨는 환경에서 사용3) @Spy주로 사용 환경: Mockito (단위 테스트)스프링 빈 등록 여부: X (별도 Bean 아님)특징:부분 가짜(Spy) 객체를 생성기본적으로 실제 메서드 동작을 유지하면서, 필요한 부분만 가짜(Stubbing)로 설정 가능복잡한 객체를 테스트할 때, 일부는 실제 로직을 실행하고 일부만 Mock 처리할 수 있어 유연함4) @SpyBean주로 사용 환경: Spring Boot Test(통합 테스트)스프링 빈 등록 여부: O (빈으로 등록됨)특징:스프링 컨텍스트 내 실제 Bean을 Spy로 교체기본적으로는 실제 로직을 수행하되, 특정 메서드만 가짜로 동작시키거나 호출을 기록할 수 있음통합 테스트 환경에서 부분 Mocking이 필요할 때 적합5) @InjectMocks주로 사용 환경: Mockito (단위 테스트)스프링 빈 등록 여부: 해당 없음특징:@Mock 또는 @Spy로 만들어진 객체들을 자동으로 주입(생성자, 세터, 필드 순)해줌예: @InjectMocks UserService라면, UserService가 의존하는 Repository나 다른 객체들을 @Mock으로 생성해 주입받을 수 있음단위 테스트에서 여러 의존 객체를 편리하게 Mock/Spy로 대체할 수 있도록 지원 🎯 Day18 미션 2: 테스트 시나리오(@BeforeEach, given, when, then) 구성요구사항아래 3개의 테스트 메서드가 있을 때, “어떤 준비 로직을 @BeforeEach로 뽑고, 어떤 부분을 각 테스트의 given절에 둘지”, “when절을 어떻게 구성할지”를 구상해보기예시 테스트 3종사용자가 댓글을 작성할 수 있다.사용자가 댓글을 수정할 수 있다.자신이 작성한 댓글이 아니면 수정할 수 없다.나의 구성@BeforeEach void init() { // 공통 데이터를 미리 생성하지 않습니다. /* * 각 테스트가 각각의 상황(사용자, 게시물, 댓글)을 자유롭게 구성할 수 있도록, * 여기서는 사전에 아무것도 세팅해두지 않습니다. * 테스트마다 필요한 데이터 타입이나 조건이 다를 수 있으므로, * 각 테스트 메서드 내부에서 직접 객체를 생성하고 준비해 주는 방식을 채택했습니다. */ } @DisplayName("사용자가 댓글을 작성할 수 있다.") @Test void writeComment() { // given // 1-1. 사용자 생성에 필요한 내용 준비 // 1-2. 사용자 생성 // 1-3. 게시물 생성에 필요한 내용 준비 // 1-4. 게시물 생성 // 1-5. 댓글 생성에 필요한 내용 준비 // when // 1-6. 댓글 생성 // then // 검증 } @DisplayName("사용자가 댓글을 수정할 수 있다.") @Test void updateComment() { // given // 2-1. 사용자 생성에 필요한 내용 준비 // 2-2. 사용자 생성 // 2-3. 게시물 생성에 필요한 내용 준비 // 2-4. 게시물 생성 // 2-5. 댓글 생성에 필요한 내용 준비 // 2-6. 댓글 생성 // when // 2-7. 댓글 수정 // then // 검증 } @DisplayName("자신이 작성한 댓글이 아니면 수정할 수 없다.") @Test void cannotUpdateCommentWhenUserIsNotWriter() { // given // 3-1. 사용자1 생성에 필요한 내용 준비 // 3-2. 사용자1 생성 // 3-3. 사용자2 생성에 필요한 내용 준비 // 3-4. 사용자2 생성 // 3-5. 사용자1의 게시물 생성에 필요한 내용 준비 // 3-6. 사용자1의 게시물 생성 // 3-7. 사용자1의 댓글 생성에 필요한 내용 준비 // 3-8. 사용자1의 댓글 생성 // when // 3-9. 사용자2가 사용자1의 댓글 수정 시도 // then // 검증 }@BeforeEach정말 모든 테스트에 공통으로 필요한 세팅(예: DB clean-up, 동일한 테스트 데이터 세팅 등)이면 이곳에 배치.하지만 테스트마다 시나리오가 크게 다른 경우, 오히려 각 테스트 내부(given 절)에서 데이터를 생성하는 편이 가독성과 유연성이 좋아질 수 있음.given 절테스트에 필요한 사전 조건(사용자, 게시물, 댓글 등) 및 Mock/Stubbing이 필요하다면, 해당 로직을 배치.시나리오별로 조금씩 다른 상황을 구성할 때, @BeforeEach보다 각 테스트 내부의 given 부분에 명시적으로 작성하는 방식이 선호됨.when 절실제 테스트 대상 메서드를 호출하는 구간. 한 개의 테스트에서 “정확히 하나의 when 절”을 유지해, 명확히 어떤 동작을 검증하는지 드러내도록 함.then 절결과 검증(Assertions) 수행. 저장된 데이터 확인, 예외 발생 여부 확인, 반환값 확인 등.미션 회고레이어별 특징을 자기 언어로 설명단순히 문서상 개념을 반복하기보다, 실제 현업/프로젝트에서 마주치는 구조를 떠올리며 다시 풀어내보니, 각 계층이 왜 필요한지, 테스트를 어떻게 접근해야 하는지 더 명확해졌습니다.Mock/Spy 애너테이션 정리정리를 해보니 실제로 코드에 적용할 때 어떤 상황에서 무엇을 써야 하는지가 더 분명해졌습니다.@MockBean/@SpyBean이 스프링 컨텍스트를 사용하는 통합 테스트에서 유용하지만, 속도와 Bean 교체 이슈가 있어 주의가 필요하다는 점이 인상적이었습니다.3가지 테스트 시나리오에서 @BeforeEach & given/when/then 배치시나리오에 따라 @BeforeEach를 최소화하거나, 여러 테스트에 공통적으로 필요한 부분만 추출하는 방식이 각각 장단점이 있음을 확인했습니다.테스트 구조를 “given-when-then”으로 고정하면 가독성이 올라가지만, 너무 세세한 단계 분리는 오히려 장황해질 수 있으므로 균형을 잡아야겠습니다.회고칭찬하고 싶은 점레이어별 테스트 기법과 Mock/Spy 전략이 한층 체계적으로 잡혔습니다.실제 코드를 작성하며, “가짜 객체를 어디까지 써야 하나? 외부 연동은 어떻게 테스트하나?” 같은 고민을 많이 해 봐서, 앞으로는 목적에 맞는 테스트를 설계하는 능력이 향상될 것 같습니다.아쉬웠던 점 & 보완 계획Mock 남발 시 발생할 수 있는 문제(설정 복잡도 상승, 실제 로직 변경 시 Stubbing 깨짐)를 더 구체적인 예제로 다뤄보면 좋을 것 같습니다.@BeforeEach와 개별 테스트 내부 given 절의 적절한 균형점을 찾으려면, 실제 현업 수준의 다양한 테스트 케이스를 더 경험해 봐야겠습니다.다음 목표예외 상황 테스트 강화단순 성공 케이스뿐 아니라, 비정상 입력이나 예외 케이스를 좀 더 체계적으로 정리하고 테스트에 반영. 도메인 별로 테스트 슬라이싱@DataJpaTest, @WebMvcTest 등 스프링이 제공하는 슬라이스 테스트 방식을 적극 활용해 보기.테스트 실행 속도 최적화통합 테스트와 단위 테스트를 적절히 조합하여, 빠르면서도 신뢰성 있는 테스트 환경을 구축해 보기.이번 4주차에는 계층형 아키텍처를 다시 한번 복습하면서 Mock/Spy, @MockBean/@SpyBean 등 스프링 테스트 환경에서 자주 쓰이는 기법들을 정리하고 적용해 보았습니다. 학습 내용과 미션을 통해 각 레이어가 가진 의미와 책임이 더욱 또렷해졌고, 가짜 객체를 어떻게 잘 활용해야 하는지 감이 잡힌 것 같습니다.앞으로도 학습 내용을 기록하고 회고하면서, 점점 더 탄탄한 테스트 코드를 작성해 가도록 하겠습니다.감사합니다!
백엔드
・
인프런
・
워밍업클럽
・
스터디
・
백엔드
・
클린
・
테스트
・
코드
・
발자국
・
회고
・
3기
2025. 03. 23.
0
인프런 워밍업 클럽 스터디 3기 - 백엔드 클린 코드, 테스트 코드 3주차 발자국
Readable Code: 읽기 좋은 코드를 작성하는 사고법Practical Testing: 실용적인 테스트 가이드강의와 함께한 인프런 워밍업 클럽 스터디 3기 - 백엔드 클린 코드, 테스트 코드 (Java, Spring Boot)3주차 발자국 입니다.학습 내용 요약이번 주에는 Spring & JPA 환경에 계층형 아키텍처(Layered Architecture) 및 테스트 기법(통합 테스트, MockMvc, JPA 테스트 등)을 학습했습니다.1⃣ Spring & JPASpring이 제공하는 IoC/DI 개념과 ORM(JPA, Hibernate) 기반 데이터 접근 방식을 이해했습니다.엔티티(Entity) 설계를 통해 도메인 모델링을 하고, 리포지토리(Repository) 계층으로 데이터 CRUD를 담당하도록 분리했습니다. 2⃣ Layered ArchitecturePresentation Layer(Controller)Business Layer(Service)Persistence Layer(Repository)컨트롤러는 요청/응답을 처리하고, 비즈니스 로직은 서비스가 담당하며, 데이터 접근은 리포지토리를 통해 수행합니다.3⃣ 테스트 코드 작성단위 테스트: 특정 클래스나 메서드 단위 검증. 예: JUnit + AssertJ 사용통합 테스트: 스프링 컨텍스트를 로드해 여러 레이어(Controller, Service, Repository)의 협력을 검증.MockMvc를 사용해 테스트하는 방식을 배웠습니다.4⃣ 유효성 검증(Bean Validation)DTO에서 @NotEmpty, @NotNull, @Positive 등 애노테이션을 사용하여 입력값을 검증하고,RestControllerAdvice로 BindException 등을 처리하여 예외 상황 시 API 응답을 표준화했습니다.✨ 학습 회고Spring & JPA 학습을 통해, 계층형 아키텍처 전체 흐름을 이해해야 함을 느꼈습니다.테스트 코드에서, 단위 테스트와 통합 테스트의 경계와 필요성을 체감했습니다.JUnit5 + AssertJ로 간단한 메서드 단위 검증을 할 때는 편리했지만, MockMvc로 검증하거나, @DataJpaTest로 테스트할 때는 더 많은 설정이 필요했습니다. 그만큼 테스트 커버리지가 넓어지며 신뢰도가 높아진다는 장점을 느꼈습니다.Validation과 ExceptionHandler를 통해 잘못된 입력을 막고, 표준화된 에러 응답을 제공하는 프로세스가 얼마나 중요한지 깨달았습니다.미션 해결 과정🎯 스터디카페 이용권 선택 시스템 테스트 미션미션스터디카페 이용권 선택 시스템의 테스트 코드를 작성하는 미션.미션 목표메시지 출력 테스트할인 로직 테스트사물함 선택 테스트 해결 과정OutputHandlerTest환영 메시지 + 공지사항 “프리미엄 스터디카페” 등의 문구가 제대로 찍히는지 확인.주문 내역 요약에 “이용 내역”, “이용권”, “사물함”, “총 결제 금액” 같은 키워드가 포함되는지 검증.StudyCafePassOrderTest할인율 및 가격 계산이 의도대로 동작하는지 (예: (250,000 + 10,000) - 할인액 25,000 = 235,000) 확인.사물함이 있는지 없는지에 따라 Optional가 올바르게 반환되는지 테스트.StudyCafeSeatPassTest할인율이 0일 때 할인 금액이 0원인지, 0.1이면 정상 할인액이 계산되는지.시간권(HOURLY)은 cannotUseLocker() == true, 고정석(FIXED)은 false인지 등을 테스트로 보장. 회고도메인 로직이 잘 분리되어 있어 테스트 작성이 수월했습니다. 만약 복잡한 코드가 여기저기 있었다면, 테스트도 훨씬 어렵고 중복될 뻔했습니다. 또한 예외 처리 부분에 대한 테스트 코드의 필요성도 느껴졌습니다. 예외 상황에 대한 흐름을 명확하게 검증하고, 안정적인 동작을 보장하기 위해서는 예외 처리에 대한 테스트도 함께 구성되어야 한다고 생각했습니다.🔗 테스트 미션 깃허브 링크회고스스로 칭찬하고 싶은 점도메인 흐름 파악: Controller → Service → Repository 순으로 데이터가 흐르는 구조를 더욱 제대로 이해하게 되었습니다. 다양한 스프링 테스트 어노테이션(WebMvcTest, SpringBootTest, DataJpaTest 등)을 직접 적용하며, 각 용도에 맞게 테스트를 구분해본 경험이 유익했습니다.아쉬웠던 점 & 보완할 점테스트 격리: 통합 테스트 시 상태를 초기화하는 과정이 번거로웠고, 테스트 순서에 따라 의존성이 생기지 않도록 더 철저히 관리해야 함을 느꼈습니다.테스트 시나리오 다양성 부족: 대부분 해피 케이스 위주로 검증한 감이 있어, 더욱 폭넓은 예외 상황에 대한 시나리오를 추가 작성하면 안정성이 높아질 것 같습니다.다음 주 학습 목표Mock과 Test Double: Mock과Test Double 개념을 학습하고, 적절히 사용해 볼 예정입니다.테스트 환경 독립성:테스트 시 독립적인 환경을 유지하는 방법을 살펴볼 예정입니다.테스트 개선 기법:조금 더 간결하고 가독성 높은 테스트 작성을 시도해 볼 계획입니다.이번 주에는 Spring & JPA 환경에서의 테스트 코드 작성을 중점적으로 경험 했습니다. 이를 통해 서비스가 단계적으로 확장되더라도, 테스트 코드가 안정적인 작동을 보장할 수 있다는 점을 다시금 깨달았습니다. 앞으로도 테스트 우선 방식으로 새로운 기능이나 리팩토링을 진행해, 안전하고 효율적인 협업 환경을 유지해 나가겠습니다.감사합니다!
백엔드
・
인프런
・
워밍업클럽
・
스터디
・
백엔드
・
클린
・
테스트
・
코드
・
발자국
・
회고
・
3기
2025. 03. 16.
0
인프런 워밍업 클럽 스터디 3기 - 백엔드 클린 코드, 테스트 코드 2주차 발자국
Readable Code: 읽기 좋은 코드를 작성하는 사고법Practical Testing: 실용적인 테스트 가이드강의와 함께한 인프런 워밍업 클럽 스터디 3기 - 백엔드 클린 코드, 테스트 코드 (Java, Spring Boot)2주차 발자국 입니다.학습 내용 요약이번 주는 클린 코드 작성법에 이어 코드 구조와 리팩토링, 그리고 테스트 코드 작성법을 본격적으로 학습했습니다.1⃣ 주석의 양면성주석은 코드 자체로 표현하기 어려운 의사결정의 히스토리를 기록할 때 사용해야 합니다.자주 변하는 정보는 주석으로 표현하면 안 되고, 주석은 코드와 함께 꾸준히 관리해야 합니다.부정확한 주석은 주석이 없는 코드보다 더 해롭습니다.2⃣ 변수와 메서드의 나열 순서변수는 사용하는 순서대로 나열하고, 공개 메서드를 먼저 배치하여 외부 세계에 제공하는 기능을 명확히 드러냅니다.비공개 메서드는 공개 메서드에서 호출되는 순서로 나열하여 코드 읽기 흐름을 자연스럽게 만듭니다.나열 순서도 의도를 표현하는 중요한 수단이 될 수 있음을 배웠습니다.3⃣ 패키지 나누기패키지 구조는 도메인 문맥을 반영하여 설계하며, 너무 잘게 나누거나 너무 크게 뭉치는 것을 지양해야 합니다.팀 내 합의가 중요하며 초기 설계 단계에서 패키지 구조를 충분히 고민해야 합니다.4⃣ 적정 기술의 사용모든 기술은 적정한 수준과 시점에서 활용해야 하며, 무리한 추상화나 오버 엔지니어링은 오히려 개발 효율성을 떨어뜨립니다.클린 코드는 만능 해결책이 아니며, 상황과 목적에 맞는 균형 잡힌 접근이 중요합니다.5⃣ 테스트 코드와 TDD자동화된 단위 테스트(JUnit5, AssertJ)를 통해 신뢰성과 유지보수성을 높이고, 빠른 피드백을 받을 수 있습니다.TDD(Test Driven Development)를 통한 선(先) 테스트 작성, 후(後) 기능 구현 방식을 이해했습니다.Given-When-Then 구조를 이용한 BDD(Behavior Driven Development) 스타일로 작성하면 도메인 로직을 명확히 표현할 수 있습니다.미션 해결 과정🎯 스터디카페 이용권 선택 시스템 리팩토링 미션미션'스터디 카페 이용권 선택 시스템'의 기존 시스템에 중복된 로직과 혼재된 책임을 명확히 분리하고, 객체지향 원칙을 준수하여 코드 구조를 개선하는 리팩토링 미션입니다.접근 관점추상화 레벨 맞추기: 중복 로직을 별도 메서드로 추출했습니다.객체의 책임 분리: StudyCafeOrder 객체를 추가해 할인 계산 로직을 전담했습니다.DIP(의존성 역전 원칙): 파일 접근 로직을 인터페이스(StudyCafeRepository)로 추상화하여 확장성을 확보했습니다.해결 과정1. 기존의 복잡한 분기 조건(if-else)을 메서드로 깔끔하게 정리했습니다.Before: if (HOURLY) { ... } else if (WEEKLY) { ... } else if (FIXED) { ... } 로직이 길고, 사물함 로직도 그 안에서 처리.After:public void run() { // 1) passType 선택 (시간권/주단위/고정석) StudyCafePassType passType = inputHandler.getPassTypeSelectingUserAction(); // 2) pass 선택 StudyCafePass selectedPass = selectPass(passType); // 3) 사물함 선택 (고정석 only) StudyCafeLockerPass lockerPass = maybeSelectLocker(selectedPass); // 4) 주문 객체 생성 & 결과 출력 StudyCafeOrder order = new StudyCafeOrder(selectedPass, lockerPass); outputHandler.showOrderResult(order); }이점: 각 단계가 메서드로 분리되어 이해하기 쉬움. passType별로 중복된 분기 로직이 크게 줄었음. 2. 할인 및 총금액 계산 로직을 전담하는 객체를 만들어 로직을 명확히 했습니다.Before: 할인금액, 총액 계산 로직이 OutputHandler.showPassOrderSummary 안에 위치.After: StudyCafeOrder라는 도메인 객체가 이 로직을 담당.public int getDiscountAmount() { return (int) (selectedPass.getPrice() * selectedPass.getDiscountRate()); } public int getTotalPrice() { int discountPrice = getDiscountAmount(); int lockerPrice = (lockerPass != null) ? lockerPass.getPrice() : 0; return selectedPass.getPrice() - discountPrice + lockerPrice; }이점: 필요 시 “할인 정책”이 바뀌어도 StudyCafeOrder만 수정하면 되며, 출력부는 깔끔하게 유지. 3. 파일 접근 방식을 인터페이스와 구현체로 나누어 유지보수와 확장성을 높였습니다.Before: StudyCafeFileHandler라는 클래스에서 직접 파일 접근 + 변환.After: StudyCafeRepository(추상) + StudyCafeFileRepository(구현).StudyCafePassMachine은 StudyCafeRepository repository에만 의존 → DIP 준수.향후 DBRepo나 InMemoryRepo 추가 시 StudyCafeRepository만 구현하면 됨. 회고리팩토링을 통해 코드 가독성과 유지보수성이 눈에 띄게 향상되었습니다. 특히 객체지향 원칙(SRP, DIP)을 철저히 적용한 결과, 각 객체가 단 하나의 명확한 책임을 가지면서, 향후 변경이 발생해도 최소한의 수정만으로 대응 가능해졌습니다. 규칙의 절대성이 아닌 "적정선"을 찾는 것이 매우 중요하다는 점을 깊이 깨달았습니다.🔗 리팩토링 미션 깃허브 링크회고스스로 칭찬하고 싶은 점학습한 내용을 즉시 코드에 적용하며 원칙을 체화하고자 노력한 점단순한 기능적 개선을 넘어, 구조적이고 근본적인 리팩토링을 고민한 점아쉬웠던 점 & 보완할 점코드 리뷰에서 코드 확장성과 유지보수성을 높이기 위한 설계가 부족했음을 느꼈습니다. 다음에는 코드 확장성과 유지보수성을 고려한 구조를 더욱 더 고민해 보겠습니다.지나치게 추상화에 치우치는 경향을 느꼈고, 다음에는 더 실용적인 균형을 유지하려 합니다.다음 주 학습 목표다음 주부터는 TDD 기반으로 더욱 실질적이고 구체적인 테스트 코드 작성을 연습할 계획입니다.리팩토링과 테스트가 결합된 실습을 통해, 코드를 더욱 안정적으로 관리하는 방법을 익힐 예정입니다.이번 주 스터디를 통해 "코드는 언제나 적정한 수준의 추상과 구체 사이에서 균형을 잡아야 한다"는 중요한 원칙을 다시 한번 실감했습니다. 앞으로도 적절한 추상화와 구체적인 구현 사이의 "적정선"을 끊임없이 고민하며, 클린하고 안정적인 코드를 지속적으로 작성하는 개발자가 되도록 노력하겠습니다.감사합니다!
백엔드
・
인프런
・
워밍업클럽
・
스터디
・
백엔드
・
클린
・
테스트
・
코드
・
발자국
・
회고
・
3기
2025. 03. 09.
0
인프런 워밍업 클럽 스터디 3기 - 백엔드 클린 코드, 테스트 코드 1주차 발자국
Readable Code: 읽기 좋은 코드를 작성하는 사고법Practical Testing: 실용적인 테스트 가이드강의와 함께한 인프런 워밍업 클럽 스터디 3기 - 백엔드 클린 코드, 테스트 코드 (Java, Spring Boot)1주차 발자국 입니다.학습 내용 요약이번 주 동안 [Readable Code: 읽기 좋은 코드를 작성하는 사고법] 강의를 통해 크게 두 가지 주제를 학습했습니다.1⃣ 추상과 구체추상(abstract)은 코드나 로직에서 세부 구현을 생략하고 핵심 의미만 전달하는 방식입니다.구체(concrete)는 코드나 로직의 세부적이고 구체적인 구현을 모두 드러내는 표현 방식입니다.이 개념을 코드에 적용하면 다음과 같은 장점이 있습니다.추상화된 이름 짓기와 메서드 분리를 통해 복잡한 코드를 단순화할 수 있습니다.협업 시 팀원에게 필요한 수준의 정보를 전달할 수 있어 효율적입니다.코드 유지보수가 용이해지고 가독성이 높아집니다.2⃣ 논리와 사고의 흐름, SOLID 원칙📌 논리, 사고의 흐름Early Return: 불필요한 else를 제거하고 조건이 맞지 않을 때 즉시 return해 코드 흐름을 명료하게 유지하는 방법입니다.사고의 Depth 줄이기: 중첩 분기문과 반복문의 깊이를 줄이고, 변수를 필요한 곳 가까이에 선언해 인지적 부담을 낮춥니다.부정어 최소화: 긍정적이고 직관적인 표현으로 메서드 이름을 짓고, 부정 연산자(!) 사용을 줄입니다.📌 SOLID 원칙SRP(단일 책임 원칙): 클래스가 하나의 명확한 책임만 가지도록 설계해야 합니다.OCP(개방-폐쇄 원칙): 기존 코드를 수정하지 않고 확장할 수 있도록 설계합니다.LSP(리스코프 치환 원칙): 자식 클래스가 부모 클래스를 대체해도 프로그램이 정상 작동하도록 설계합니다.ISP(인터페이스 분리 원칙): 사용하지 않는 메서드를 구현하도록 강요받지 않도록 인터페이스를 작게 분리합니다.DIP(의존성 역전 원칙): 고수준 모듈과 저수준 모듈 모두 추상화된 인터페이스에 의존하여 유연성을 높입니다.미션 해결 과정 & 회고Day 2 미션: 추상과 구체 예시미션추상과 구체 강의를 듣고, 생각나는 추상과 구체의 예시 작성해결 과정일상적인 예시를 통해 추상과 구체 개념을 명확히 이해하려고 했습니다.회사 출근이나 배달 음식 주문 등 일상 사례를 통해, 간략히 표현된 추상과 세부적인 표현을 비교하여 정리했습니다.관점 및 이유개념을 일상적이고 친숙한 예시로 접근함으로써 이해를 높이고자 했습니다.추상과 구체의 차이를 분명히 보여줄 수 있는 사례를 선택했습니다.회고일상적 예시 덕분에 개념 이해가 훨씬 쉬워졌고, 추상화의 필요성과 구체적 표현의 중요성을 체감할 수 있었습니다. 앞으로 코드 작성 시 이 관점을 계속 유지할 생각입니다.🔗 Day 2 미션 블로그 링크 Day 4 미션 1: 코드 리팩토링미션중첩 if-else 구조의 주문 검증 메서드 리팩토링접근 관점코드의 인지적 복잡성을 줄이기 위해 Early Return 방식을 사용했습니다.각 조건이 독립적이며, 직관적으로 위에서 아래로 읽히도록 조건 검사를 순서대로 배치했습니다.추가로 주문(Order) 객체가 자기 책임을 다하는 방식으로 책임을 위임하는 방법도 고려했습니다.해결 방법public boolean validateOrder(Order order) { // 주문 항목 유무 if (order.getItems() == null || order.getItems().isEmpty()) { log.info("주문 항목이 없습니다."); return false; } // 총 가격 검증 if (order.getTotalPrice() 회고Early Return을 적용한 결과, 가독성이 확연히 높아졌고 코드를 읽는 과정이 더 명확해졌습니다. 앞으로도 복잡한 조건 로직을 만날 때 적극적으로 이 방법을 적용할 생각입니다. Day 4 미션 2: SOLID 원칙 정리미션SOLID 원칙을 자신만의 언어로 정리해결 과정각 원칙을 "왜 이런 원칙이 필요한지" 스스로에게 질문하며 이해했습니다.실제 코드 예시를 작성하여, SOLID 원칙을 위반한 코드와 준수한 코드를 비교하면서 학습했습니다.회고원칙을 "내 언어"로 설명하면서, 이전에 막연히 알고 있던 개념들이 훨씬 더 명확하게 다가왔습니다. 단순히 원칙을 외우는 것과 달리, 직접 코드를 작성하고 리팩토링 과정을 거치며 더 깊이 이해할 수 있었습니다.🔗 Day 4 미션 블로그 링크회고이번 주 스터디를 통해 코드를 작성할 때 "추상과 구체의 적정선을 찾는 것이 중요하다"는 점을 깊이 깨달았습니다. 특히 코드가 너무 구체적이면 가독성이 떨어지고, 너무 추상적이면 구체적인 동작이 모호해진다는 점을 직접 실습과 미션을 통해 경험할 수 있었습니다. 또한 Early Return이나 중첩 최소화 등 작은 코드 개선만으로도 가독성이 크게 높아지는 것을 체감할 수 있어 유익했습니다.스스로 칭찬하고 싶은 점미션을 통해 배운 내용을 바로 적용하여 리팩토링하면서 개념을 내 것으로 만들려고 노력한 점이 뿌듯했습니다.SOLID 원칙을 단순히 암기하는 것이 아니라, 스스로 이해하고 예시 코드까지 작성하면서 명확히 내 언어로 표현한 것이 좋았습니다.아쉬웠던 점 & 보완할 점가끔 너무 추상화에 집중하다 보니 코드가 실제 구현에서 너무 멀어지는 경우가 있어, 구체성과 추상성을 더 균형 있게 다루는 연습이 필요하다고 느꼈습니다.SOLID 원칙을 실제 프로젝트 코드에 적용해 보지 못한 점은 조금 아쉬웠습니다. 앞으로 개인 프로젝트에 적용하여 체득할 계획입니다.다음 주 학습 목표다음 주는 강의의 다음 섹션인 "리팩토링" 부분을 학습할 예정입니다. 특히 리팩토링 실습을 통해 SOLID 원칙을 실제 코드에 적용하고, 객체 지향적 사고방식을 내 것으로 만드는 것을 목표로 삼고 있습니다.소감이번 주 강의를 통해 코드 가독성이라는 주제가 얼마나 중요하고 광범위한지 실감했습니다. 앞으로도 이번 학습 내용을 프로젝트에 적극 적용하며, 읽기 좋고 유지보수하기 좋은 코드를 꾸준히 작성하는 습관을 들이겠습니다.감사합니다!
백엔드
・
인프런
・
워밍업클럽
・
스터디
・
백엔드
・
클린
・
테스트
・
코드
・
발자국
・
회고
2024. 11. 07.
1
인프런 워밍업 클럽 스터디 2기 - 백엔드 프로젝트 후기
입문자를 위한 Spring Boot with Kotlin - 나만의 포트폴리오 사이트 만들기강의와 함께한 인프런 워밍업 클럽 스터디 2기 - 백엔드 프로젝트 (Kotlin, Spring Boot) 후기 입니다. 소개인프런 워밍업 클럽 스터디 2기를 마무리하며, 한 달 동안 진행된 백엔드 프로젝트와 함께한 학습 여정을 회고해보고자 합니다. 이번 스터디는 Kotlin과 Spring Boot를 활용해 실습 중심의 백엔드 개발 프로젝트를 진행하면서, 많은 성장을 경험할 수 있었습니다. 매주 주어진 미션을 해결하고 강의를 수강하며 그 과정에서 직면했던 도전과 성공, 그리고 배움을 함께 나누겠습니다. 1주차: 웹 개발의 기본과 레이어드 아키텍처의 이해1주차는 Spring Boot를 기반으로 한 웹 개발의 기본 개념을 익히고, MVC 패턴과 레이어드 아키텍처를 학습하는 데 집중했습니다. 웹 서비스의 구조와 클라이언트, 서버, 데이터베이스 간의 상호작용을 이해하며, 클라이언트와 서버 간의 데이터 흐름을 REST API의 개념으로 체계화할 수 있었습니다. MVC 패턴의 중요성과 레이어드 아키텍처의 역할을 명확히 이해하면서, 어떻게 하면 유지보수성이 높은 코드를 작성할 수 있을지 깊이 고민하는 시간이 되었습니다.개인적으로 가장 보람찼던 부분은 이론 학습 후 즉시 실습을 통해 프로젝트에 적용해보면서, 개념을 구체화하고 몸에 익힐 수 있었던 점입니다. 특히 강의 내용을 프로젝트에서 직접 구현해보며, 이론과 실제가 어떻게 연결되는지를 깨닫는 과정이 매우 유익했습니다. 2주차: JPA와 API 설계의 시작2주차에는 JPA를 활용하여 데이터베이스와 상호작용하는 방법을 학습하고, CRUD 기능을 직접 구현해보며 JPA의 기본 개념에 익숙해질 수 있었습니다. 실습 프로젝트를 통해 엔티티 간의 관계 설정과 데이터베이스 초기화를 진행하면서 JPA의 다양한 기능을 경험하였고, 강의를 통해 학습한 내용을 실습에 적용하며 개발자로서의 자신감을 쌓을 수 있었습니다.또한, 이번 주에는 REST API를 설계하고 구현하는 미션을 수행했습니다. 사용자와 게시글, 댓글을 관리하는 여러 API를 설계하면서, RESTful한 접근 방식을 유지하기 위해 고민했습니다. API 설계 시 직관적이고 간결한 엔드포인트를 유지하려고 노력한 덕분에, 이후 테스트 코드 작성 및 검증 과정에서도 큰 어려움 없이 진행할 수 있었습니다. 3주차: 컨트롤러 개발과 API 테스트 코드 작성3주차에는 Spring Boot와 JPA를 사용하여 컨트롤러를 개발하고, 다양한 엔드포인트를 구현했습니다. 이와 함께 Thymeleaf를 활용해 프론트엔드 작업을 진행하며, 백엔드와 프론트엔드의 유기적인 연결을 실습해 볼 수 있었습니다.특히 이번 주에는 테스트 코드 작성의 중요성을 다시 한번 실감하게 되었습니다. API의 동작을 검증하는 테스트 코드를 작성하면서, 예상하지 못했던 예외 상황을 처리하고 기능을 보완하는 과정을 통해 코드의 완성도를 높일 수 있었습니다. Thymeleaf를 사용해 프론트엔드를 구성하는 데 있어 부족함이 있었지만, 이를 보완해가며 더 나은 결과물을 만들 수 있었던 점이 인상 깊었습니다. 4주차: 프론트엔드 템플릿 작업과 배포 경험마지막 4주차에는 Thymeleaf의 fragment 기능을 사용하여 HTML 구조를 모듈화하는 작업을 진행했습니다. 프론트엔드를 구성하며 공통 레이아웃을 재사용 가능한 형태로 분리해 유지보수성을 높였고, Docker와 Nginx를 활용해 배포 작업까지 경험할 수 있었습니다. 직접 Docker 이미지를 빌드하고, MySQL 컨테이너와 연동하여 실제 서비스를 배포하는 경험은 매우 실질적이었고, 개발자로서 한 단계 더 성장할 수 있는 계기가 되었습니다.이번 스터디를 통해 가장 크게 느낀 점은 개발 과정에서 이론과 실습의 균형이 얼마나 중요한지였습니다. 각 주차마다 학습한 내용을 프로젝트에 직접 적용해보고, 그 과정에서 발생한 문제들을 해결하면서 실력을 쌓을 수 있었습니다. 또한, API 설계와 테스트 코드 작성, 그리고 실제 배포까지 전 과정을 경험하면서 백엔드 개발의 흐름을 체계적으로 이해할 수 있었습니다.이번 스터디에서 성실히 참여한 결과, 우수러너로 선정되는 영광도 누릴 수 있었습니다. 앞으로도 이번 스터디에서 얻은 배움을 바탕으로 꾸준히 학습하고 성장해 나가고자 합니다. 인프런 워밍업 클럽 스터디를 통해 함께한 모든 분들께 감사드리며, 이후에도 지속적으로 배움을 나누고 함께 성장해 나가기를 희망합니다.
백엔드
・
인프런
・
인프런워밍업클럽
・
스터디2기
・
워밍업
・
백엔드
・
프로젝트
・
후기
2024. 10. 27.
1
인프런 워밍업 클럽 스터디 2기 - 백엔드 프로젝트 4주차 발자국
입문자를 위한 Spring Boot with Kotlin - 나만의 포트폴리오 사이트 만들기강의와 함께한 인프런 워밍업 클럽 스터디 2기 - 백엔드 프로젝트 (Kotlin, Spring Boot) 4주차 발자국 입니다. 강의 수강이번 주에는 Spring Boot와 JPA를 사용한 뷰 개발 및 Thymeleaf를 활용한 프론트엔드 템플릿 작업에 중점을 두었습니다. 주로 Bootstrap 템플릿을 가져와 활용하고, Thymeleaf의 fragment 기능을 사용하여 재사용 가능한 HTML 구조를 만들었으며, 배포 작업까지 진행했습니다.주요 학습 내용Bootstrap 템플릿 가져오기 및 적용Bootstrapmade 사이트에서 Admin 템플릿을 다운로드하여 프로젝트에 적용했습니다.템플릿 파일을 프로젝트의 resources 디렉토리에 추가하고, 필요한 CSS, JS 파일들을 적용했습니다.Thymeleaf와 프론트엔드 분리 작업Thymeleaf의 fragment 기능을 활용해 HTML 구조를 모듈화하였습니다. head, header, footer 등 여러 공통 요소를 별도의 fragment로 분리하여 재사용성을 높였습니다.타임리프 문법을 통해 동적으로 페이지를 구성하고 유지보수가 용이하도록 개선했습니다.프로젝트 배포 작업Docker를 이용해 MySQL을 컨테이너로 실행하고, Spring Boot 애플리케이션을 Docker 이미지로 빌드하여 배포했습니다.Nginx를 사용해 80포트와 8080포트를 연결하여 클라이언트 요청을 처리하도록 설정했습니다.Let's Encrypt를 이용해 HTTPS 인증서를 설정하고, 웹사이트를 HTTPS로 안전하게 접근할 수 있도록 구성했습니다. 회고이번 주는 Thymeleaf와 Bootstrap을 활용하여 프론트엔드 작업을 진행하며, 뷰를 모듈화하고 효율적으로 구성하는 방법을 배울 수 있었습니다. 특히 Thymeleaf의 fragment 기능은 HTML 코드의 재사용성과 유지보수성을 크게 개선해 주어 프로젝트의 구조를 더욱 체계적으로 만들 수 있었습니다. 또한, 직접 템플릿을 가져와 수정하고 적용하는 과정을 통해 프론트엔드 개발 역량을 키울 수 있었고, Docker와 Nginx를 이용한 배포 작업을 통해 실제 서비스 환경에서의 배포 경험도 쌓을 수 있었습니다.칭찬할 점Thymeleaf를 사용하여 공통 레이아웃을 fragment로 분리하여 재사용성을 높인 점.Bootstrap 템플릿을 프로젝트에 무리 없이 적용하고, 필요한 수정 작업을 성공적으로 수행한 점.Docker와 Nginx를 이용한 배포 과정을 원활히 진행한 점.아쉬웠던 점 및 보완할 점Thymeleaf 문법에 대한 익숙함이 아직 부족하여 일부 동적 작업에서 어려움을 겪었습니다. 추가적인 연습과 학습을 통해 더 익숙해질 필요가 있습니다.프론트엔드 요소의 커스터마이징에 있어 부족함을 느꼈으며, 좀 더 세밀한 스타일 수정 방법을 연구하고자 합니다. 미션이번 주 미션은 삽입, 수정, 삭제 REST API를 개발하고, 이를 검증하는 테스트 코드를 작성하는 것이었습니다. API는 각 기능별로 POST, PUT, DELETE 요청을 처리하도록 설계하였으며, 모든 케이스에 대해 성공적으로 테스트를 완료했습니다.미션 과정게시글 삽입 API 설계 (POST /api/posts)새로운 게시글을 작성하고, 작성된 데이터를 JSON 형태로 반환하도록 설계했습니다.게시글 수정 API 설계 (PUT /api/posts/{postId})특정 게시글을 수정하고, 수정된 결과를 반환하는 기능을 구현했습니다.게시글 삭제 API 설계 (DELETE /api/posts/{postId})특정 게시글을 삭제하고, 삭제 후 상태 코드 204 No Content를 반환하도록 했습니다.테스트 코드 작성 및 검증각 엔드포인트에 대해 최소 3개의 테스트 케이스를 작성해, 다양한 상황에서의 시스템 동작을 검증했습니다.필수 값이 누락된 경우, 잘못된 ID로 요청했을 때의 예외 처리 등도 함께 테스트하여 안정성을 확보했습니다. 미션 과정추가적으로 가상 프로필을 나의 프로필로 바꾸기 미션과 배포한 프로젝트 공유하기 미션까지 모두 마무리하며, 스터디의 모든 미션을 마무리 하였습니다. 이 발자국을 끝으로 모든 미션과 발자국, 강의 수강 100% 까지 마무리 하며, 스터디를 성공적으로 완주하였습니다. 회고이번 미션을 통해 RESTful한 API 설계 및 테스트의 중요성을 다시 한번 체감할 수 있었습니다. 또한, Thymeleaf를 활용해 프론트엔드를 모듈화하고 코드의 재사용성을 높이는 작업이 실제 프로젝트에서 어떻게 활용될 수 있는지 직접 경험할 수 있는 시간이었습니다. 배포 작업을 통해 실제 서비스 환경에서 발생할 수 있는 문제들을 다루며, 더 나은 품질의 소프트웨어를 개발하기 위한 실무 경험을 쌓을 수 있었습니다. 앞으로 JPA 연관 관계에 대한 이해를 깊이 있게 다지고, 테스트 코드의 가독성을 더욱 높이는 방법을 연구하여 더 나은 품질의 소프트웨어를 개발해 나가겠습니다.
백엔드
・
인프런
・
워밍업클럽
・
스터디
・
백엔드
・
프로젝트
・
발자국
・
회고
・
SpringBoot