🎁 모든 강의 30% + 무료 강의 선물🎁

블로그

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

Day 12섹션6. Spring & JPA 기반 테스트Persistence Layer 테스트 (1)Persistence Layer 테스트 (2)DataJpaTest는 JPA 관련 빈들만 등록해주기 때문에 SpringBootTest보다 빠르다@ActiveProfiles 어노테이션을 사용해 활성화 할 프로파일을 설정할 수 있다.리스트를 검증할 때는 size를 먼저 검증하고extracting + contains 조합을 많이 사용한다extracting 내부에는 검증할 필드들을 넣어주면되고contains는 다양한 api들이 있다.여기서 사용한 containsExactlyInAnyOrder()는 순서 상관없이 인자로 들어온 튜플들이 정확히 존재하는지extracting에 적은 필드의 순서대로 적어주면 된다.Day 13Business Layer 테스트 (1)Persistence Layer는 비즈니스 가공 로직이 포함되어서는 안 된다.Business Layer는 Persistence Layer와의 상호작용(Data를 읽고 쓰는 행위)을 통해 비즈니스 로직을 전개Business Layer 테스트 (2)SpringBootTest는 자동롤백이 안달려있고DataJpaTest는 자동롤백이 달려있음Day 14Business Layer 테스트 (3)Day 15Presentation Layer 테스트 (1)모킹: 가짜 객체로 대신하여 정상 동작할거야 라는 것을 가정하고프레젠테이션 레이어즉, 테스트하고자 하는 레이어에만 집중해서 테스트하겠다readOnly = true : 읽기전용 트랜잭션CRD 작업이 동작 X / only ReadJPA : CUD 스냅샷 저장, 변경감지 등을 안해도 되기 때문에 성능 향상CQRS - Command(CUD) / Query(R)미션Day 11사용자의 입력값은 무조건 불신을 깔고 들어가야한다는 강사님의 말씀이 떠올랐다.그래서 InputHandler를 테스트하고자 했다.StudyCafeIOHandler를 테스트해야하나 고민했지만, 통합해주는 역할일뿐입력에 대한 최종 책임은 InputHandler에 있다고 판단했다.기존 InputHandler에는private static final Scanner SCANNER = new Scanner(System.in);로 스캐너가 생성되어있어서 테스트할 때 nextLine() 예외가 발생했다.private final Scanner scanner; public InputHandler(Scanner scanner) { this.scanner = scanner; }사용자 입력같은 테스트하기 어려운 영역을 분리하자는 강사님의 말씀이 떠올라 외부에서 주입받도록InputHandler를 변경해주었다.또 해피 케이스말고 예외 케이스를 생각해 1~3 이외에 다른 입력시 들어왔을 때 예외가 잘 발생하는지 확인했다.또 사물함을 사용할 수 있는 패스권인지 확인하는 메서드를 검증하기 위해StudyCafePassType에 대한 테스트를 진행했다.확실히 enum 타입으로 객체로 만드니 관련 로직을 위한 공간이 생겨 테스트가 용이하구나를 느꼈다.그리고 가장 중요한 금액관련 테스트를 진행하고자했다.금액 관련 테스트를 StudyCafePassOrder에서 전부 다 진행할까 했지만,하나의 테스트는 하나의 책임만 가져야한다고 생각해서,StudyCafeSeatPass, StudyCafeLockerPass에서 각각 진행했다.테스트를 진행하면서 궁금한 점은StudyCafeLockerPass lockerPass = StudyCafeLockerPass.of(StudyCafePassType.FIXED, 4, 10000);처럼 of 메서드에서 csv 파일에 맞게 직접 입력해주는게 맞을지아니면 LockerPassFileReader에서 객체를 찾아내서 테스트 하는게 맞을지 궁금하다.그리고 DisplayName 짓는 것이 생각보다 되게 까다로웠다.테스트는 쉽지않다. 하지만 올바르지 못한 테스트는 오히려 혼란을 일으킬뿐이라는 것은 알게되었다.제대로 된 테스트를 작성하도록 노력하자.모킹 테스트를 왜 해야는지는 아직 명확하게 와닿진 않는다.알 때 까지 복습하자.

백엔드워밍업클럽테스트발자국박우빈

szun

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

강의명: 시작하는 PM/PO들에게 알려주고 싶은, 프로덕트의 모든 것코치: 김민우링크: https://www.inflearn.com/course/%EC%8B%9C%EC%9E%91%ED%95%98%EB%8A%94-pmpo-%EB%AA%A8%EB%93%A0%EA%B2%83/dashboard3주차 강의 포인트 지표(Metric)란?우리 사업, 제품의 현황과 성과를 측정해서 정량화 한 것Acquisition, Activation, Engagement, Retention, Monetization 등의 지표를 상시로 모니터링 하면우리 제품의 현황이 어떠한지, 어떤 추이로 변화하고 있는지 알 수 있고,지표를 토대로 의사결정을 하고 과업을 수행할 수 있음.Proxy 지표가정이 많이 들어간, 간접적인 지표.즉, 어떠한 값을 측정하고 싶은데 해당 값이 정량적이지 않아 직접 측정이 어려울 때, 가정을 포함하여 설정한 대체 지표.Proxy 지표는 완벽하지 않을 수 있지만, 설정했다는 것 자체만으로 의미가 있음. 모든 프로덕트에 적용할 수 있는 대표적인 지표 5가지Acquisition질문 1. 우리는 충분히 많은 신규 유저/고객을 획득하고 있는가?질문 2. 신규 유저/고객을 비용 효율적으로 획득하고 있는가?CAC (고객 획득 비용)Customer Lifetime Value (고객 생애 가치)Payback Period (비용 회수 기간)Activation신규 획득한 사용자들이 프로덕트의 핵심 가치를 경험하는 습관을 형성하는 것Activation은 Retention에 영향을 주는 핵심 요소 중 하나임.Activation 3단계Step Moment사용자가 제품의 핵심 가치를 경험하기 위한 '준비'를 마친 순간Aha Moment사용자가 '처음으로' 제품의 핵심 가치를 경험한 순간Habit Moment사용자가 제품의 핵심 가치를 경험하는 '습관'을 형성한 순간Engagement사용자들이 프로덕트에 관심을 갖고, 이용하고, 관계를 맺는 것얼마나 많은 유저들이 이용하는가 (Breadth)제품을 얼마나 깊이 있게 이용하는가 (Depth)얼마나 자주 이용하는가 (Frequency)성공적으로 과업을 완수하는가 (Efficiency)Retention고객(사용자)들이 제품을 계속해서 이용하는 것Retention Rate특정 기간 동안 고객(사용자)들이 유지되는 비율리텐션율을 이야기할 때는 항상 기간과 분자, 분모를 정의해야 함.Retention 측정/관찰 방식Cohort Retention (코호트 리텐션)특정 cohort user들이 시간이 경과함에 따라 유지되는 비율Retention CurveDay N RetentionN일째 유지된 사용자의 비율Bracket(Bounded) Retention어떤 기간 내에 유지된 사용자 비율Unbounded(On and After) Retention Monetization대표적인 Monetization의 지표들매출기간별 매출 성장률Paying User 수인당 매출(객단가) 지표Net Revenue Retention Metric Hierarchy, Input/Output 지표Metric Hierarchy : 지표의 위계 구조하나의 output 지표에는 다양한 input 지표들이 작용한 결과.수식으로 명확하게 표현되지 않는 지표 간의 관계도 존재.이는 프로덕트에 대해 많은 고민과 리서치를 하고 여러 케이스에 대한 실험으로 알아내는 수 밖에 없음. Product Analytics의 주요 개념들데이터는 투자가 필요한 자원이다.어떤 데이터를 축적할 것인지, 어떤 형태로 어디에 데이터를 기록할 것인지 정의가 필요.Product Analytic의 주요 개념들Event-Based Analytics이벤트를 기반으로 하는 데이터 분석Event유저와 제품 사이에 일어나는 상호작용Event Property이벤트에 수반되는 상세 정보User Property유저에 대한 정보Date TypeClient-side/Server-side Tracking어디에서 데이터를 트래킹할 것인지에 대한 기준 Event Taxonomy Event Taxonomy 설계란?어떤 event, 어떤 property를 트래킹할 것인지 구체적으로 정의하는 작업 두 가지 접근 방식Top-Down 접근데이터를 활용하는 '목적'에서부터 정의하는 것Bottom-Up 접근'우리 제품의 주요 이벤트는 무엇인가?'에서 시작 Naming Convention다음 요소들을 고려해야 함.일관성이벤트 이름을 설정할 때 일관성을 지켜야 함명확성부여한 이름이 무엇을 뜻하는지 명확하게 설정해야 함이벤트를 얼마나 잘게 쪼갤 것인가?각 행동이 '구분되는 서로 다른 종류의 행동'인지, 아니면 '본질적으로 같은 행동'인지 판단해서 설정하기  Event Taxonomy 문서를 만들 시 꼭 포함시켜야 하는 정보Naming Convention이벤트 정보이번트 프로퍼티 정보유저 프로퍼티 정보3주차 회고  이번 주차 강의는 최근 데이터 분석을 공부하고자 마음을 먹은 나에게 많은 도움을 주는 강의였다. 데이터 분석을 공부하고 있지만 어떤 지표를 기준으로 데이터를 수집하여 분석을 해야 하는지 감이 잡히지 않는 상황에서 이번 내용들은 길잡이가 되어 주었다. 하지만 Proxy 지표와 같은 가정을 필요로 하는 데이터들을 다루는 것에 대해서는 여전히 걱정이 많다. 객관적이고 명확한 정보에서 편안함을 느끼는 나의 경우, 반대의 성향을 지닌 정보들에서는 불안함이 수반되는 것 같은 느낌을 받는다. PM에 대해 공부할수록 PM이라는 직무가 굉장히 추상적인 것 같다는 인상을 계속해서 받게 된다. 이걸 해결하기 위한 방법은 아무래도 경험뿐이라는 생각이 드는데, 이런 모호함을 떨쳐내는 것이 나의 숙제이지 않을까 싶다.

기획 · PM· PO

[워밍업 클럽 3기 BE code] 2주차

테스트가 필요한 이유사람이 애플리케이션을 실행하면서 잘 작동되는지 확인하는 작업은 놓칠 수도 있고, 시간이 오래 걸려 비효율적이다.테스트 코드 작성의 이점빠른 피드백: 코드 수정 후 바로 검증 가능자동화: 반복적인 수동 테스트가 불필요안정성: 코드 변경이 발생해도 기존 기능이 정상 동작하는지 보장디버깅 용이: 문제 발생 시 원인 파악이 쉬움단위 테스트작은 코드 단위를 독립적으로 검증하는 테스트 → 검증 속도가 빠르고 안정적이다.관련 라이브러리JUnit5: 단위 테스트를 위한 테스트 프레임워크AssertJ: 테스트 코드 작성을 원활하게 돕는 라이브러리수동 테스트 vs 자동화된 테스트 비교수동 테스트사람이 직접 성공/실패를 판단실수 가능성 높음반복이 어려움시간이 오래 걸림자동화된 테스트시스템이 자동으로 판단실수 가능성 낮음반복이 쉬움소요 시간이 짧음 자동화된 테스트를 활용하면 수동 테스트의 한계를 극복할 수 있다.자동화 테스트를 작성하면 테스트 결과만 확인하면 되므로 빠르고 정확하다.테스트 케이스 세분화하기해피 케이스(정상 동작)*만 작성하지 않고 예외 케이스(예상하지 못한 상황)도 함께 테스트해야 한다.TDD (Test Driven Development)프로덕션 코드보다 테스트 코드를 먼저 작성하여, 테스트가 구현 과정을 주도하도록 하는 개발 방법론빠른 피드백을 통해 코드 품질을 높이고, 유연한 설계를 가능하게 함TDD의 장점내 코드의 피드백을 빠르게 받을 수 있음리팩토링이 쉬워짐 → 테스트가 있으므로 기존 기능이 정상 동작하는지 보장됨테스트 가능한 코드 설계 → 단일 책임 원칙(SRP)에 맞게 코드 구조를 고민하게 됨디버깅 시간 단축 → 기능 개발 중 발생하는 문제를 사전에 방지할 수 있음기능 구현 방식 비교방식 장점 단점선 기능 구현, 후 테스트 작성장점 : 구현이 직관적이고 빠르게 가능단점 : 테스트 누락 가능성, 특정 케이스만 검증, 잘못된 구현 발견 지연  선 테스트 작성, 후 기능 구현 (TDD)장점 : 테스트가 어려운 영역을 미리 발견하여 설계를 개선할 수 있음단점 : 초기 개발 속도가 다소 느릴 수 있음TDD의 핵심 원칙: Red → Green → Refactor1⃣ Red (실패하는 테스트 작성)테스트 코드를 먼저 작성하고 실행 → 아직 기능이 없으므로 테스트가 실패해야 한다.2⃣ Green (기능 구현하여 테스트 통과)테스트가 통과하도록 최소한의 기능을 구현한다.3⃣ Refactor (리팩토링)중복 제거, 코드 개선을 통해 더 나은 구조로 변경한다.리팩토링 후에도 테스트가 통과하는지 확인한다.테스트는 [문서]다테스트 코드는 해당 기능의 동작을 설명하는 문서 역할을 한다.@DisplayName을 활용하여 테스트 목적을 명확히 하자.@DisplayName("회원 가입 시 이메일이 중복되면 예외가 발생한다") @Test void 회원가입_이메일중복_예외발생() { // Given // When // Then }생각 정리테스트의 중요성에 대해서는 인지하고 있었지만 테스트 코드 작성이 항상 귀찮고 어렵게 느껴졌었다. 하지만 테스트 코드를 작성하고 결과를 확인하는 과정 자체가 더 나은 프로덕션 코드를 만드는 과정이 될 수 있다는 것을 깨달았다.특히 TDD의 경우에는 기능이 없는데 테스트 코드를 먼저 작성하는게 이해가 안 됐었는데 테스트를 실패하는 최소한의 기능을 먼저 만들고 점진적으로 더 나은 코드 작성 및 설계를 할 수 있도록 유도하는 과정이라는 것을 알게 되었다. 강의https://www.inflearn.com/course/practical-testing-%EC%8B%A4%EC%9A%A9%EC%A0%81%EC%9D%B8-%ED%85%8C%EC%8A%A4%ED%8A%B8-%EA%B0%80%EC%9D%B4%EB%93%9C/dashboard

jinwoo2511

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

'그림으로 쉽게 배우는 운영체제'[섹션 01]운영체제란??컴퓨터의 하드웨어적인 요소들과 소프트웨어적인 요소들을 효율적으로 운영하여 관리함으로써 사용자가 시스템을 이용하는데에 편리함을 제공하는 시스템 소프트웨어를 말합니다.운영체제 종류 및 특징 유닉스/리눅스대화식 운영체제사용자가 명령을 입력하면 시스템이 명령을 수행다중작업한번에 하나 이상의 작업을 수행할 수 있음다중 사용자여러 사람이 동시에 각각 작업을 수행할 수 있음이식성90%이상 C언어로 구성, 시스템이 모듈화 되어 있어 다른 하드웨어 기종에 쉽게 이식 가능계층적 파일 시스템 제공계층적 트리 구조를 가짐유닉스와 리눅스의 차이점리눅스는 대부분 무료이지만 유닉스는 대부분 유료리눅스는 개발자 및 일반 사용자가 사용, 유닉스는 대형 시스템 관리자가 사용리눅스는 오픈 소스 개발, 유닉스는 사업자에 의해 배포 비용 수반리눅스는 BASH 쉘 사용, 유닉스는 커맨드 기반 윈도우그래픽 사용자 인터페이스(GUI) 제공키보드없이 마우스로 아이콘이나 메뉴를 선택하여 작업 수행 가능선점형 멀티 태스킹 방식 제공동시에 여러개의 프로그램을 실행하며, 운영체제가 각 작업의 CPU 이용시간 제어자동 감지 기능 (Plug & Play)하필요한 시스템 환경을 운영체제가 자동으로 구성OLE(Object Linking and Embedding) 사용개체를 현재 작성 중인 문서에 자유롭게 연결 또는 삽입 가능 IOSiOS는 사용자 경험과 인터페이스에 매우 중점을 둠하드웨어 및 소프트웨어 최적화강력한 보안 및 프라이버시 기능OS는 앱스토어를 통해 다양한 애플리케이션을 제공하며, 개발자들에게 큰 수익 창출의 기회를 제공  운영체제 특징사용자 편리성한정된 시스템 자원을 효과적으로 사용할 수 있도록 관리 및 운영인터페이스 기능컴퓨터 시스템과 사용자를 연결스케줄링자원의 현재 상태를 파악, 자원 분배를 위한 스케줄링을 담당자원관리프로세스 관리메모리 관리하드웨어 관리파일 시스템 관리입출력 프로그램 관리제어기능입출력 장치와 사용자 프로그램을 제어 운영체제 역사 1940년도애니악 개발애니악 특징운영체제가 없는 최초의 컴퓨터응용 프로그램이 시스템 자원을 제어스위치와 배선을 연결해서 프로그래밍문제 발생 시 종이에 작업 후 테스트 마침 => 기간이 많이 소모되는 단점30톤으로 수많은 진공관으로 구성되어 있기 때문에, 하드웨어 비용이 굉장히 비쌈 1950년대 초진공관과 전선으로 만들어진 논리 회로를 아주 작은 크기로 만든 직접회로 개발CPU와 메모리가 존재하지만 키보드와 모니토는 존재하지 않음펀치 카드에 프로그래머가 카드의 구멍을 뚫어 프로그래밍컴퓨터가 카드를 읽고 계산 후 결과를 라인 프린터로 출력기존 스위치 배선 작업보다 편해짐 1950년도 중후반이전 작업은 오퍼레이터의 오버헤드가 너무 큼싱글 스트림 배치 시스템 개발여러개의 프로그램을 순서대로 실행해서 결과를 한번에 확인할 수 있는 시스템 I/O Device 컨트롤러를 만들어 입출력 중에도 CPU가 계산할 수 있도록 만듦입출력 작업이 끝나면 CPU에게 인터럽트 신호를 주고 인터럽트를 받은 CPU는 다시 처리를 하는 식으로 발전입출력에도 CPU를 기다려야 하는 작업이 필요하다는 단점이 있음   1960년도 이후메모리 침범 이슈 발생다중 프로그램으로 인한 메모리 주소 이슈 발생하드웨어적으로 베이스 레지스터라는 것을 추가해서 프로그램의 시작 주소를 저장하고 모든 프로그램은 영번지에서 실행한다고 가정CPU의 사용률과 효율성을 중요한 문제로 인식 1970년도 이후개인용 컴퓨터의 시대가 시작저렴해진 컴퓨터의 개인 소유가 쉬워짐애플의 매킨토시와 마이크로소프트의 MS-DOS가 많이 사용매킨토시는 GUI를 도입해서 굉장한 인기CPU 사용률과 비용 절감을 위한 노력으로 오늘날의 운영체제가 탄생  운영체제의 구조커널운영체제의 핵심 기능들이 모여있는 컴퓨터 프로그램커널의 기능프로세스 관리기억 장치 관리주변장치 관리파일 관리사용자로부터 자신을 보호하기 위한 시스템 콜이라는 인터페이스 보유시스템 콜을 이용하면 커널에서 제공하는 write() 함수를 쓰게 되는데 하드디스크의 빈 공간에 저장 인터페이스GUI그래픽 사용자 인터페이스의 약자로 말 그대로 그래픽으로 된 인터페이스윈도우나 맥OS와 같이 그래픽으로 커널과 상호작용하기 때문에 일반 사용자도 사용하기가 쉬움 CLI명령줄 인터페이스의 약자요즘은 리눅스는 GUI 환경을 제공하지만 많은 사용자들이 리눅스의 CLI를 선호 컴퓨터 하드웨어와 구조현재 컴퓨터는 프로그램 내장 방식의 폰 노이만 구조폰 노이만은 CPU와 메모리 사이를 버로 연결함버스는 데이터를 전달하는 통로프로그램 내장 방식은 프로그램을 메모리에 올려서 실행시키는 방식메인보드다른 하드웨어를 연결하는 장치다양한 단자가 있음ex) 출력단자, 그래픽 카드 단자, USB 단자, 사운드 단자 등등장치간의 데이터를 전송하는건 메인보드의 버스가 담당CPU1. 산술논리 연산 장치데이터 연산 담당2. 제어 장치모든 장치들의 동작 지시 및 제어3. 레지스터CPU 내에서 계산을 위해 임시적으로 보관하는 장소 RAM랜덤으로 데이터를 읽어도 저장된 위치와 상관 없이 읽는 속도가 동일전력이 끊기면 데이터를 잃어버리기 때문에 메인 메모리로 사용 ROM전력이 끊겨도 데이터 영구 보관데이터를 한번 쓰면 수정 불가  컴퓨터 부팅 과정ROM에 저장된 BIOS 가 실행됨바이오스 - BIOS (Basic Input Output System)메모리와 CPU 레지스터를 초기화 시킨다.디스크로부터 부트 로더를 불러 온다바이오스는 롬에 저장되어 있기 때문에 램에 저장된 정보와는 달리, 컴퓨터를 끄더라도 그 내용이 지워지지 않음  주요 하드웨어에 이상이 없는지 확인 후 부팅 이상이 있을 경우 경고음을 내면서 부팅이되지 않음운영체제가 2개 설치되어 있을 경우, 운영체제를 선택하는 화면이 활성화됨 부팅 후 실행되는 모든 프로그램은 램에 올라와서 운영체제에 의해 관리  폴딩주기적으로 CPU가 입출력 관리자에게 주기적이고 지속적으로 입출력이 왔는지 확인폴링 방식의 단점은 주기적으로 CPU가 확인해줘야 하니 성능이 좋지 않다는 단점이 존재    인터럽트폴링 방식의 단점을 해결한 방식입출력이 완료되었을 때 CPU에게 신호를 주어 인터럽트 서비스 루틴을 실행하는 방식서비스루틴 이란?특정 인터럽트가 들어오면 그 인터럽트를 처리하는 함수  [섹션 02]프로그램이란?하드디스크 등과 같은 저장장치에 저장된 명령문의 집합체 프로세스란?하드디스크에 저장된 프로그램이 메모리에 올라갔을 때 실행중인 프로그램메모리도 사용하고 운영체제의 CPU 스케줄링 알고리즘에 따라서 CPU도 사용하고 필요에 따라 입력과 출력을 하기 때문에 능동적인 존재코드 영역, 데이터 영역, 스택 영역, 킵 영역을 갖고 있음코드 영역자신을 실행하는 코드가 저장되어 있고 데이터 영역은 전역 변수와 스태틱 변수가 저장스택 영역지역 변수와 함수 호출을 했을 때 필요한 정보들이 저장힙 영역프로그래머가 동적으로 메모리를 할당ex) C언어에서 malloc, free 함수를 호출하면 힙 영역에 자원을 할당, 해제 컴파일 과정고급 언어(예: C, C++)로 작성된 소스 코드를 기계어(바이너리 코드)로 변환하는 과정4단계(전처리 → 컴파일 → 어셈블 → 링크)로 진행 1. 전처리 (Preprocessing)소스 코드에서 전처리 지시문(#으로 시작하는 문장)을 처리하는 단계생성 파일 : .i 로 끝남 2. 컴파일 (Compilation)전처리된 코드를 어셈블리 코드(assembly language)로 변환하는 단계생성 파일 : .s 로 끝남주요 작업문법 검사 및 오류 확인최적화 수행어셈블리 코드 생성 3.어셈블 (Assembling)어셈블리 코드를 기계어(오브젝트 파일, .o 또는 .obj)로 변환하는 단계입니다.생성 파일 : .o 로 끝남주요 작업명령어를 바이너리 코드로 변환오브젝트 파일 생성4. 링크 (Linking)여러 개의 오브젝트 파일과 라이브러리를 결합하여 실행 파일(Executable, .exe 또는 a.out)을 생성하는 단계생성 파일 : .exe, .out 로 끝남  멀티 프로그래밍과 멀티 프로세싱 멀티 프로그래밍하나의 CPU가 여러 프로세스를 번갈아 실행특징CPU 이용률 극대화비선점형 스케줄링에 많이 사용병렬 처리가 아닌 시분할 방식 멀티 프로세싱여러 개의 CPU가 동시에 여러 프로세스를 처리특징병렬 처리 가능선점형 스케줄링 적용다중 코어 시스템에서 사용PCB (Process Control Block)운영체제가 각 프로세스를 관리하기 위해 유지하는 데이터 구조체프로세스의 상태, 메모리 정보, CPU 레지스터 정보 등을 저장구성 요소PID (Process ID)프로세스 상태프로그램 카운터CPU 레지스터메모리 관리 정보입출력 상태 정보우선순위 프로그램 카운터가 필요한 이유는 어떤 프로세스가 실행되다가 다른 프로세스에게 CPU를 뺏기고 다시 실행될 때 원래 실행하던 명령어가 실행되어야 하기 때문에 프로그램 카운터가 꼭 있어야 함 프로세스 상태1. 생성새로운 프로세스 생성       2. 준비CPU 할당을 기다리는 상태 3.실행CPU가 프로세스를 실행 중4.대기/O 작업 등으로 대기 중5. 완료실행이 끝난 상태컨텍스트 스위칭 (Context Switching)CPU가 현재 실행 중인 프로세스를 중단하고, 다른 프로세스로 전환하는 작업PCB에 현재 프로세스의 상태 정보를 저장하고, 다음 프로세스의 상태 정보를 불러옴절차1. 현재 프로세스의 상태 저장 (PCB)2. 새로운 프로세스의 PCB 불러오기3. CPU 레지스터 및 메모리 정보 복원4. 새로운 프로세스 실행 프로세스 생성과 종료프로세스 생성 과정1. 부모 프로세스가 fork() 또는 CreateProcess() 호출2. 운영체제가 PCB 생성3. 메모리 할당 및 초기화4. Ready 상태로 전환프로세스 종료 과정1. 프로세스 작업 완료 또는 오류 발생2. 운영체제가 자원 회수3. PCB 제거4. Terminated 상태로 전환 쓰레드 (Thread)프로세스 내에서 실행 흐름 단위독립적인 스택과 레지스터를 가짐같은 프로세스의 쓰레드는 코드, 데이터, 힙 영역 공유쓰레드 종류커널 레벨 쓰레드: 운영체제에서 관리유저 레벨 쓰레드: 사용자 공간에서 관리  [섹션 03] CPU 스케줄링 이란?CPU 스케줄링은 운영체제가 CPU를 여러 프로세스에게 효율적으로 할당하는 기법이는 시스템 성능을 최적화하고 공정성을 보장하며, 응답 시간과 처리율을 개선하는 역할   2. 스케줄링 목표CPU 이용률CPU를 최대한 활용하도록 함처리량단위 시간당 처리할 수 있는 프로세스 수를 최대화대기 시간 최소화프로세스가 CPU를 기다리는 시간을 줄임응답 시간 최소화사용자의 요청에 빠르게 반응공정성모든 프로세스가 공정하게 CPU를 할당받도록 보장 다중 큐 (Multi-Queue)다중 큐 스케줄링은 프로세스를 여러 개의 큐로 분류하고, 각 큐에 다른 스케줄링 정책을 적용하는 방식ex) 시스템 프로세스는 높은 우선순위를 가진 큐에서 실행되고, 사용자 프로세스는 낮은 우선순위 큐에서 실행  FIFO (First In First Out)먼저 들어온 프로세스가 먼저 실행됨 (큐의 구조와 유사)장점: 구현이 간단함단점: 짧은 작업보다 긴 작업이 먼저 실행되면 Convoy Effect 발생 (짧은 작업이 뒤에서 오래 대기하는 문제) SJF (Shortest Job First)실행 시간이 가장 짧은 프로세스를 먼저 실행장점: 평균 대기 시간을 최소화단점: 실행 시간이 긴 프로세스가 계속 뒤로 밀려 기아 현상(Starvation) 발생 가능 타임 슬라이스 (Time Slice)란?타임 슬라이스는 CPU가 각 프로세스에 할당하는 최대 실행 시간을 의미타임 퀀텀(Time Quantum) 이라고도 부르며, 보통 밀리초(ms) 단위로 설정한 프로세스가 타임 슬라이스만큼 실행된 후, 문맥 전환(Context Switch) 을 통해 다른 프로세스로 넘어감 타임 슬라이스의 특징짧은 타임 슬라이스빠른 응답성을 제공하지만 문맥 전환 비용이 증가긴 타임 슬라이스문맥 전환 비용이 줄지만 응답 시간이 길어짐적절한 타임 슬라이스 값을 설정하는 것이 중요일반적으로 타임 슬라이스는 문맥 전환 비용보다 충분히 커야 효율적 타임 슬라이스가 적용되는 스케줄링 알고리즘1. RR (Round Robin)2. MLFQ (Multi-Level Feedback Queue) RR (Round Robin)일정한 시간동안 프로세스를 실행한 후, 다음 프로세스로 전환 장점: 공정성이 보장됨 (모든 프로세스가 CPU를 일정 시간 동안 사용할 기회 제공)단점: 시간 할당량이 너무 크면 FIFO와 유사해지고, 너무 작으면 문맥 전환(Context Switch) 비용 증가 MLFQ (Multi-Level Feedback Queue)여러 개의 큐를 사용하여, 우선순위를 동적으로 조정하는 방식 처음에는 높은 우선순위 큐에서 실행CPU를 많이 사용하면 낮은 우선순위 큐로 이동I/O 중심 프로세스는 높은 우선순위를 유지 장점: CPU 및 I/O 중심 프로세스를 균형 있게 처리할 수 있음단점: 정책을 잘못 설계하면 특정 프로세스가 기아 상태에 빠질 가능성이 있음  '그림으로 쉽게 배우는 자료구조와 알고리즘(기본편)' 자료구조와 알고리즘이란?자료구조(Data Structure): 데이터를 효율적으로 저장하고 관리하는 방법알고리즘(Algorithm): 문제를 해결하는 절차나 방법 시간 복잡도(Time Complexity)란?알고리즘이 실행되는 연산 횟수를 입력 크기(n)에 따라 분석한 것주로 빅오 표기법(O-notation)으로 표현배열(Array) 개념같은 자료형의 연속된 메모리 공간을 차지하는 데이터 구조빠른 접근(O(1)) 가능, 하지만 삽입/삭제(O(N))이 느림 배열 구현#include <iostream> using namespace std; int main() { int arr[5] = {1, 2, 3, 4, 5}; cout << "배열 요소 출력: "; for(int i = 0; i < 5; i++) { cout << arr[i] << " "; } return 0; } 연결 리스트(Linked List) 개념노드(Node)들이 포인터로 연결된 구조삽입/삭제가 빠름(O(1)), 접근 속도가 느림(O(N))단일 연결 리스트(Singly Linked List), 이중 연결 리스트(Doubly Linked List) 등이 있음연결리스트 구현#include <iostream> using namespace std; struct Node { int data; Node* next; Node(int val) : data(val), next(nullptr) {} }; class LinkedList { public: Node* head; LinkedList() : head(nullptr) {} void insert(int data) { Node* newNode = new Node(data); newNode->next = head; head = newNode; } void remove(int data) { Node* temp = head; Node* prev = nullptr; while (temp && temp->data != data) { prev = temp; temp = temp->next; } if (!temp) return; // 삭제할 노드 없음 if (prev) prev->next = temp->next; else head = temp->next; // 첫 번째 노드 삭제 시 delete temp; } void display() { Node* temp = head; while (temp) { cout << temp->data << " -> "; temp = temp->next; } cout << "NULL\n"; } }; int main() { LinkedList list; list.insert(10); list.insert(20); list.insert(30); list.display(); list.remove(20); list.display(); return 0; } 스택(Stack) 개념LIFO(Last In First Out) 구조push(삽입), pop(제거), top(최상단 요소 확인)스택 구현 (C++)#include <iostream> #include <stack> using namespace std; int main() { stack<int> s; s.push(10); s.push(20); s.push(30); cout << "Top: " << s.top() << endl; s.pop(); cout << "Top after pop: " << s.top() << endl; return 0; }  큐(Queue) 개념FIFO(First In First Out) 구조push(삽입), pop(제거), front(첫 요소), back(마지막 요소)큐 구현 (C++)#include <iostream> using namespace std; class Queue { private: struct Node { int data; Node* next; Node(int val) : data(val), next(nullptr) {} }; Node *frontNode, *rearNode; public: Queue() : frontNode(nullptr), rearNode(nullptr) {} void enqueue(int data) { Node* newNode = new Node(data); if (!rearNode) { frontNode = rearNode = newNode; return; } rearNode->next = newNode; rearNode = newNode; } void dequeue() { if (!frontNode) return; Node* temp = frontNode; frontNode = frontNode->next; if (!frontNode) rearNode = nullptr; delete temp; } int front() { return (frontNode) ? frontNode->data : -1; } bool isEmpty() { return frontNode == nullptr; } }; int main() { Queue q; q.enqueue(10); q.enqueue(20); q.enqueue(30); cout << "Front: " << q.front() << endl; q.dequeue(); cout << "Front after dequeue: " << q.front() << endl; return 0; }  덱(Deque) 개념양방향 삽입/삭제 가능한 자료구조push_front(), push_back(), pop_front(), pop_back() 제공덱 구현 (C++)#include <iostream> using namespace std; class Deque { private: struct Node { int data; Node* next; Node* prev; Node(int val) : data(val), next(nullptr), prev(nullptr) {} }; Node *frontNode, *rearNode; public: Deque() : frontNode(nullptr), rearNode(nullptr) {} void push_front(int data) { Node* newNode = new Node(data); if (!frontNode) { frontNode = rearNode = newNode; } else { newNode->next = frontNode; frontNode->prev = newNode; frontNode = newNode; } } void push_back(int data) { Node* newNode = new Node(data); if (!rearNode) { frontNode = rearNode = newNode; } else { newNode->prev = rearNode; rearNode->next = newNode; rearNode = newNode; } } void pop_front() { if (!frontNode) return; Node* temp = frontNode; frontNode = frontNode->next; if (frontNode) frontNode->prev = nullptr; else rearNode = nullptr; delete temp; } void pop_back() { if (!rearNode) return; Node* temp = rearNode; rearNode = rearNode->prev; if (rearNode) rearNode->next = nullptr; else frontNode = nullptr; delete temp; } int front() { return (frontNode) ? frontNode->data : -1; } int back() { return (rearNode) ? rearNode->data : -1; } }; int main() { Deque d; d.push_back(10); d.push_front(20); cout << "Front: " << d.front() << ", Back: " << d.back() << endl; d.pop_front(); cout << "Front after pop: " << d.front() << endl; return 0; }  해시 테이블(Hash Table) 개념키-값(Key-Value) 쌍으로 데이터를 저장하는 자료구조탐색 속도 O(1), 충돌 해결 방법 필요 (체이닝, 개방 주소법 등)해시 테이블 구현 (C++)#include <iostream> #include <vector> using namespace std; class HashTable { private: static const int SIZE = 10; vector<pair<int, int>> table[SIZE]; int hashFunction(int key) { return key % SIZE; } public: void insert(int key, int value) { int hashIndex = hashFunction(key); table[hashIndex].push_back({key, value}); } int get(int key) { int hashIndex = hashFunction(key); for (auto &p : table[hashIndex]) { if (p.first == key) return p.second; } return -1; } }; int main() { HashTable ht; ht.insert(1, 100); ht.insert(11, 200); cout << "Key 1: " << ht.get(1) << endl; cout << "Key 11: " << ht.get(11) << endl; return 0; }  셋(Set) 개념중복을 허용하지 않는 집합 자료구조삽입/삭제 O(log N) (Balanced BST 사용) 셋 구현 (C++)#include <iostream> using namespace std; class Set { private: struct Node { int data; Node* next; Node(int val) : data(val), next(nullptr) {} }; Node* head; public: Set() : head(nullptr) {} void insert(int data) { if (contains(data)) return; Node* newNode = new Node(data); newNode->next = head; head = newNode; } bool contains(int data) { Node* temp = head; while (temp) { if (temp->data == data) return true; temp = temp->next; } return false; } }; int main() { Set s; s.insert(10); s.insert(20); s.insert(10); return 0; }  

수뼈

인프런 워밍업 클럽 스터디 3기 - CS 전공지식(운영체제) <둘째 주 미션>

1. FIFO 스케줄링의 장단점이 뭔가요?장점은 쉬운 구현과 실행 결과 예측의 용이성이고, 단점은 호위 효과와 사용성 저하입니다. FCFS(First Comes, First Served) Algorithm이라고도 하는 FIFO Scheduling은 이름대로 모든 프로세스를 단일 Ready Queue에 넣고 순차 실행합니다. Time Slice, Timeout Interrupt 등을 통한 Context Switching도 구현되지 않은 때의 비선점 알고리즘인 만큼 구현이 쉽고 실행 결과 예측이 용이합니다. 그러나 Burst Time이 긴 게 먼저 오느냐, 짧은 게 먼저 오느냐에 따라 평균 대기 시간 차이가 크게 벌어지는 Convoy Effect가 발생하며, 멀티 프로세싱이 불가능하므로 사용성이 크게 저하됩니다.2. SJF를 사용하기 어러운 이유가 뭔가요?각 프로세스의 Burst Time 예측 불가능성, Burst Time이 긴 프로세스의 Starvation 때문입니다. SJF(Shortest Job First) Scheduling은 Burst Time이 짧은 프로세스를 우선 실행하는 알고리즘입니다. 따라서 각 프로세스의 Burst Time을 정확히 예측하고, 그걸 바탕으로 Ready Queue에 줄세워야 하는데 이것은 외부 환경 등에 의해 큰 영향을 받는 Burst Time 특성상 현실적으로 줄세우기가 불가능하단 것이 첫 번째 이유입니다. 두 번째로, 어찌어찌 모든 프로세스를 잘 줄세웠다고 해도 Burst Time이 아주 긴 프로세스는 최악의 경우, 컴퓨터가 종료될 때까지 단 한 번도 실행되지 못할 수도 있습니다.3. RR 스케줄링에서 타임 슬라이스가 아주 작으면 어떤 문제가 발생할까요?매우 큰 Context Switching Overhead가 발생해 시스템 성능을 크게 저하됩니다. 컨텍스트 스위칭 작업에는 CPU 레지스터나 프로그램 카운터 같은 상태 정보 저장 및 복원, 메모리 캐시 무효화 등의 오버헤드 요소가 산재해 있습니다. 만약 타임 슬라이스가 극단적으로 짧다면 CPU가 연산보다 컨텍스트 스위칭에 더 많은 리소스와 시간을 소모하게 되어 전체 시스템 처리량을 크게 낮추고 응답 시간도 늘어나게 됩니다.4. 운영체제가 MLFQ에서 CPU Bound Process와 I/O Bound Process를 어떻게 구분할까요?OS는 Timeout Interrupt가 일어난 프로세스는 CPU Bound Process로, I/O Burst가 일어난 I/O Bound Process는 I/O Bound Process로 판단합니다. 어떤 프로세스가 할당된 시간 동안 작업을 완수하지 못한 채 타임아웃 인터럽트가 발생하면, 이는 프로세스가 긴 CPU burst를 수행하고 있다는 신호이므로 CPU Bound Process로 간주되어 낮은 우선순위 큐로 이동할 가능성이 큽니다. 반면 Timeout Interrupt 전에 I/O Burst을 발생시키면 CPU를 별로 안 쓴다는 뜻이므로 I/O Bound Process로 판단되어 높은 우선순위 큐에 배치되어 빠른 응답성을 유지합니다.5. 공유자원이란 무엇인가요?여러 프로세스나 스레드가 동시에 접근하고 사용할 수 있는 하드웨어나 소프트웨어 자원을 의미합니다. CPU, 메모리, 파일, 프린터, 네트워크 등이 모든 HW 자원이 여기에 해당할 수 있습니다. 동기화 문제가 발생하지 않도록 주의해야 합니다.6. 교착상태에 빠질 수 있는 조건에는 어떤 것들이 있을까요?상호 배제(Mutual Exclusion), 비선점(No Pre-emption), 점유와 대기(Hold and Wait), 원형 대기(Circular Wait)이 있습니다. 상호 배제는 하나의 프로세스가 자원을 사용 중일 때 다른 프로세스는 해당 자원을 사용할 수 없는 조건입니다. 비선점은 한 프로세스가 자원을 할당받으면, 그 프로세스가 자원을 자발적으로 해제할 때까지 다른 프로세스가 강제로 빼앗을 수 없는 조건입니다. 점유와 대기는 프로세스가 최소한 하나의 자원을 보유한 상태에서, 추가 자원을 요청해 대기하는 상황입니다. 마지막으로 원형 대기 (Circular Wait)란 프로세스들이 점유와 대기 상태로 원형으로 대기하는 것입니다. 예를 들어, 프로세스 A가 프로세스 B가 보유한 자원을 기다리고, 프로세스 B는 프로세스 C의 자원을, 그리고 마지막으로 프로세스 C는 다시 프로세스 A의 자원을 기다리는 형태입니다.

시스템 · 운영체제운영체제

워밍업클럽3기_미션2_고객 조사 설계하기

미션고객 조사 계획을 세워보기. 조사 주제를 설정하고, 어떤 사람들을 어떤 방법으로 조사할지, 어떤 질문을 할지 설계해보고, 인터뷰 질문지까지 만들어 보기 고객 조사 계획(1) 의사결정알림 앱의 A 기능 고도화 이후, A 기능을 완료하는 사용자가 30%에서 10%로 감소하였다. 원인을 파악하고, A 기능을 완료하는 사용자를 전체 방문자의 30%로 증가 시킬 수 있는 방안을 구상한다.(2) 알아야 하는데 모르는 것사용자의 특성: A 기능을 사용하지 않거나, 혹은 사용하는 유저의 특성을 정리한다.신규 사용자의 3%만 A 기능을 활용한다. 외부요인: 기능 외의 외부 요인이 영향을 주지 않았는지 검토한다.자연 검색으로 신규 사용자 유입이 감소하여, 전반적인 기능을 사용하는 유저가 감소하였으나, A 기능의 감소폭이 가장 크다.SNS 로그인 중 페이스북 로그인으로 가입하는 사용자가 큰 폭으로 감소하였다.  기능 A의 프로세스: 기능 A의 시작부터 완료 단계에서 이탈이 가장 많은 단계가 있는지 검토한다.기능 A의 시작 화면에서 개인 설정으로 넘어가는 단계에서 80% 이상의 이탈이 발생한다.코치마크를 추가하는 등의 사후 대처를 하였으나 위의 단계에서 이탈을 막지 못했다.정리신규 사용자의 기능 A 사용 비율이 저조하다.페이스북 로그인 사용자의 이탈 등 외부 요인이 영향이 없진 않지만, 주된 원인으로 보기 어려워 로그인 이슈로 분류하여 따로 대응한다.기능A의 프로세스에서 이탈이 생기는 이유에 대해 파악이 필요하다. (3) 적합한 리서치 방법은 무엇인지 정하기기능 A에 대한 고객의 멘탈모델을 살펴보기 위해 사용성 테스트를 진행한다.(4) 리서치 방법 구체화 하기조사 대상신규 사용자 중 A 기능의 프로세스를 완료하지 않은 신규 사용자 10명 모집 대상 모집 방법A 기능의 프로세스를 완료 단계에 삽입해 둔 이벤트를 기준으로, 신규 사용자 중 해당 이벤트가 발생하지 않은 목록을 추린다.목록에 추려진 신규 사용자가 앱을 방문한 경우, 사용성 테스트에 대한 팝업 알림을 띄워 신청을 받는다.목록에 추려진 신규 사용자 중 휴대폰 번호를 등록한 유저에게 사용성 테스트 신청과 관련한 SNS를 발송한다.테스트 진행 방법테스트는 ZOOM 온라인 미팅으로 진행한다.1차로 대상자 각자 시나리오에 따라 Task를 수행하도록 하고 이를 촬영하여 공유하도록 안내한다.대상자들의 공유 영상을 확인한 이후에 2차로 개별 면담을 진행한다. 테스트 시나리오당신은 기능 A를 활용하여 10분 뒤로 알람을 설정합니다.알람은 10분 동안 울리며, 알람이 울리는 동안 화면을 이동할 수 없도록 설정합니다.인터뷰 질문앱에서 알람 설정을 위해 주로 사용하는 기능은 무엇인가요? 해당 기능으로 알림을 설정하는데 얼마나 걸렸나요? 해당 기능을 주로 사용하는 이유는 무엇인가요?A 기능의 메인 화면에서 한참을 머무르던데, 머뭇거리던 이유가 무엇이었나요?A 기능에서 개인 설정을 마무리하는 과정에서 가장 먼저 설정한 옵션은 무엇이었고, 그 이유는 무엇인가요?  

기획 · PM· PO

Jaeeun Jeong

워밍업 클럽 3기 PM/PO_미션2

미션 2. 고객 조사 설계하기여러분이 맡은 프로덕트에서 고객 조사 계획을 세워보세요. (맡고 있는 프로덕트가 없는 경우, 프로덕트를 하나 정해서 해 보세요) 조사 주제를 설정하고, 어떤 사람들을 어떤 방법으로 조사할지, 어떤 질문을 할지 설계해 보고, 인터뷰 질문지까지 만들어 보세요.조사 배경원생 관리 및 수강 과목등을 관리하는 학원 매니징 서비스의 사용성이 저조함에 원인 파악을 바탕으로 경쟁력 강화를 위한 조사조사 목적사용자가 서비스의 가치를 느끼고 지속적인 서비스 이용 시간을 늘리기 위한 리뉴얼 방안 모집조사 대상서비스에 3개월 이상 접속하지 않은 사용자/ 최근 1개월 이내에 접속한 사용자조사 방법정성 조사 : 각각 10명의 사용자와 1:1 인터뷰정량 조사 : 각각 50명의 사용자에게 설문 조사 진행 질문사용한 학원 매니징 서비스의 기능은 무엇일까요?사용한 학원 매니징 서비스의 사용 기간은 어떻게 되시나요?(몇 개월/ 몇 년 등)학원 관리에 가장 많이 사용하는 기능은 무엇일까요?타사의 학원 관리 서비스를 사용한 적이 있다면 어떤 것일까요? 저희 서비스를 어떻게 알게 되셨나요?저희 서비스를 사용 중단하게 된 주요 이유는 무엇일까요?저희 서비스에서 개선되었으면 하는 부분은 어떤 것일까요?저희 서비스의 디자인이나 사용성에 대해 어떻게 생각하시나요?저희 서비스를 사용함에 있어서 어려웠던 점은 무엇일까요?저희 서비스를 이용함에 있어서 유용하다고 생각했던 점은 무엇일까요? 저희 서비스의 어떤 부분이 리뉴얼된다면 사용 시간이 늘어날 것 같은가요?학원 관리 서비스에서 가장 중요하다고 생각하는 점은 무엇일까요? 

DABBB

[인프런 워밍업 클럽 스터디 3기] 미션 2

미션내가 맡은 프로덕트에서 고객 조사 계획 세우기조사 주제 설정조사 대상 선정질문 설계인터뷰 질문지 만들기조사 대상 서비스용어 설명고객 = 사업자회원 = 고객에게 회비 등 일정 금액을 납부하는 개인고객에게 청구서 생성 및 발송 서비스를 제공하고, 회원의 등록된 결제 정보로 결제를 대행하며, 회원의 수납 여부를 관리해주는 서비스조사 주제 설정신규 고객이 우리 제품을 사용하는 이유에 대해 조사하기로 함사용 이유에 대해 자세히 알게 되면 이를 더 개선하여 신규 고객 유입을 늘릴 수 있을 거라고 예상 조사 대상 선정최근 1개월 내에 가입한 신규 고객 중에서제품의 핵심 기능인 "청구서 생성" 기능을 1번 이상 사용한 고객생성하는 청구서 개수만큼 결제할 요금이 정해짐 -> 유료 고객으로 한정가입 후 빠르게 유료 기능을 쓸 만큼 문제를 해결하고 싶은 기대가 컸던 것으로 봄  질문 설계사업 질문 : 어떻게 하면 신규 고객을 더 많이 획득할 수 있을까?리서치 질문 : 신규 고객은 어떤 문제를 해결하기 위해 우리 제품을 사용하는가?인터뷰 질문 : 우리 서비스에 가입하게 된 결정적 이유는 무엇이었나요? 인터뷰 설문지가입하기 전부터 우리 서비스에 대해 알고 계셨나요?(알고 있었다면) 바로 가입하지 않았던 이유는 무엇인가요?비슷한 다른 솔루션을 사용해보신 경험이 있으신가요?(있다면) 왜 그 솔루션을 선택하셨나요?(있다면) 왜 그 솔루션의 사용을 중단하신 건가요?가입하기 전 망설여지는 부분이 있으셨나요?우리 서비스에 가입하게 된 결정적 이유는 무엇이었나요?사용 후 그 결정적 이유는 해소되었나요?우리 서비스의 어떤 점이 만족스러우신가요?우리 서비스의 어떤 점이 아쉬우신가요?사용하면서 어려웠던 부분이 있으신가요?  

기획 · PM· PO

gptjddl777

[인프런 워밍업 클럽 3기] PM/PO - 2주차 미션

미션 2. 고객 조사 설계하기 여러분이 맡은 프로덕트에서 고객 조사 계획을 세워보세요. (맡고 있는 프로덕트가 없는 경우, 프로덕트를 하나 정해서 해 보세요) 조사 주제를 설정하고, 어떤 사람들을 어떤 방법으로 조사할지, 어떤 질문을 할지 설계해 보고, 인터뷰 질문지까지 만들어 보세요 조사 배경 자사 교육플랫폼을 이용하는 학습자들의 사이트 이용 패턴, 학습방법을 확인해 보니 로그인 후 이미 신청 되어있는 필수로 수강해야 하는 교육만 듣고 빠져 나가는 학습자가 많은 것으로 확인메인홈 → 강의실로만 이동하고 강의(=자사 상품)를 탐색하지 않는 것으로 확인조사 목표 사용자들이 메인 홈에서 강의를 탐색하게 만들기 위한 메인 개편안에 대한 아이디어 수집조사 주제 설정 교육플랫폼 메인 홈에서 강의를 탐색하지 않는 사용자들에 대한 이해조사 대상자 자사 교육플랫폼 계정이 있는 사람 중 강의를 신청해서 들은 횟수가 최근 3개월 내 1회 미만인 대상자조사 방법 정성조사 (사용자 인터뷰) : 15명의 사용자와 1:1 인터뷰 (온/오프라인) 진행정량조사 (설문지) : 100여명 이상 대상자에게 설문조사 진행인터뷰 질문로그인 후 가장 먼저 사용하는 기능, 메뉴는 무엇인가요?메인에서 주로 탐색하는 정보는 무엇인가요?탐색하지 않는다면 그 이유는 무엇인가요?메인에서 가장 관심 있게 보거나 주로 활용하시는 기능 또는 메뉴는 무엇인가요?메인에 큐레이션 되는 콘텐츠를 관심 있게 보시는 편인가요?그렇다면 or 그렇지 않다면 그 이유는 무엇인가요?어떤 메뉴, 경로를 이용해서 강의를 탐색 하시나요?원하는 강의를 찾지 못했을 때는 어떻게 하시나요?메인 홈에 추가되었으면 하는 기능 또는 제안하고 싶으신 의견이 있으실까요?

기획 · PM· PO고객조사설계

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

React의 등장 배경 React는 상태(state) 기반으로 UI를 효율적으로 관리하도록 설계된 라이브러리 기존 자바스크립트의 문제점- DOM을 조작하는 방식이 비효율적 - 변경이 필요할때마다 전체 DOM을 다시 그려야 함 - 전체가 아닌 일부 DOM만 변경시키고 싶으면 복잡한 로직이 필요- 상태 관리의 어려움 - 복잡한 UI에서는 DOM과 데이터의 일관성을 유지하기 어려움 - 데이터와 UI가 양방향으로 상호작용하며 예측하기 어려운 상태가 됨➡ 결과적으로 유지보수가 어렵고, 재사용성이 낮음 React의 해결책- 상태(state) 기반 UI 관리 - useState를 통해 UI가 변경될 때만 렌더링 - batching : 상태를 한번에 모아서 처리- 가상 DOM을 사용한 최소한의 업데이트 - 이전 가상돔과 현재 가상돔을 비교해 필요한 부분만 업데이트 (diff) - 불필요한 DOM 조작을 최소화하여 성능 향상- 컴포넌트 단위 개발 - UI를 작은 단위로 분리하여 재사용 가능 - 각 컴포넌트의 생명주기를 관리할 수 있음 리액트 컴포넌트 생명주기컴포넌트가 생성되고 사용되고 소멸될때까지의 일련의 과정- 마운트 -> 업데이트 -> 언마운트 - 마운트: 컴포넌트를 DOM에 삽입 - 업데이트: 컴포넌트의 props나 state가 변경될 때 발생 - 언마운트: 컴포넌트를 DOM에서 제거 클래스형 컴포넌트 - React.Component를 상속받아 사용- 생명주기별로 메서드가 있으며, 각 메서드를 오버라이드 할 수 있음 함수형 컴포넌트 - 생명주기 메서드를 직접 제공하지 않음.- 훅을 사용하여 생명주기와 유사한 기능 구현 훅(Hook)은 함수형 컴포넌트에서 상태(state)와 생명주기(lifecycle) 기능을 사용할 수 있도록 해주는 React 내장 함수훅의 종류 : useEffect, useState, useMemo, useCallback특히 생명주기에 가장 직접적인 영향을 미치는 훅은 useEffect- useEffect(() => {...}, []) : componentDidMount- useEffect(() => {...}, [state]) : componentDidUpdate- useEffect(() => {{return ()=>{...};}, [state]) : componentWillUnmount  리액트 훅 useState컴포넌트의 상태(state)를 관리하는 Hook- 함수형 컴포넌트에서 상태를 관리할 수 있도록 함- 상태가 변경되면 해당 컴포넌트가 리렌더링됨- useState로 관리하는 값은 **React 내부에서 관리됨- setState를 호출하면 비동기적으로 업데이트(batch 처리) 됨 - batch 처리란? : 리액트 랜더링 최적화 기법중 하나로, 여러번의 setState()호출이 있을때, 각각 개별적으로 랜더링을 발생시키지 않고 한번만 렌더링 되도록 묶어서 처리 상태란 무엇인가?- 사용자의 응답, api 응답 등에 의해 변경되며, ui를 동적으로 변경시키는 핵심 데이터- 리액트는 useState로 상태를 내부적으로 저장하고, 이 상태가 바뀔때 컴포넌트를 리랜더링한다.  상태의 종류- 로컬상태 : 개별 컴포넌트 내부에서 관리되는 상태- 전역상태 : 여러 컴포넌트가 공유하는 상태- 서버상태 : 서버에서 가져오는 상태- UI상태 : UI관련상태 (모달이 열렸는지, 토글이 열렸는지)  useState와 리액트 생명주기- 초기 렌더링 (mounting) → useState의 초기값 설정- 업데이트 (updating) → setState 호출 시 리렌더링됨- 언마운트 (unmounting) → 상태 해제  useEffect컴포넌트의 부수 효과(side effect)를 관리하는 Hook- 함수형 컴포넌트에서 컴포넌트 생명주기를 관리할때 사용됨- API 호출, 이벤트 리스너 등록, DOM 조작 등 비동기 작업을 처리할 때 사용- useEffect는 렌더링 후 실행되며, 의존성 배열(`deps`)을 통해 실행 조건을 제어 가능- 함수를 리턴할 경우, 언마운트 이후 실행됨useEffect(() => {...}); // 매 렌더링마다 실행 useEffect(() => {...}, []); // 마운트 시 1회 실행 useEffect(() => {...}, [count]); // count가 변경될 때만 실행 useEffect(() => { return () => {...}; }, []); // 언마운트 시 실행  useCallback> 함수를 메모이제이션하여 불필요한 리렌더링을 방지하는 Hook- 함수가 매번 새로 생성되는 것을 방지- React.memo()와 함께 사용하여 최적화 가능 - 상태가 변경되거나 props가 변경되면 리랜더링 발생 - 이때 props로 객체나 함수(참조형)을 넘기게 되는 경우 props는 얕은 복사를 진행하기에 매번 리랜더링을 할 수밖에 없음 이때 메모이제이션을 사용하면 불필요한 랜더링 방지 가능 useCallback과 리액트 생명주기- 컴포넌트가 마운트될 때 초기화됨- 의존성 배열이 변경되면 새로운 함수로 업데이트됨 useRef> DOM 요소에 직접 접근하거나, 값이 유지되지만 리렌더링을 유발하지 않는 변수를 관리하는 Hook- DOM 요소를 직접 조작하는 데 사용- 렌더링 간 유지되는 값을 저장할 때도 사용- 값이 변경되어도 컴포넌트가 리렌더링되지 않음 useRef와 리액트 생명주기- 컴포넌트가 마운트될 때 초기화됨- 리렌더링과 무관하게 값을 유지   

프론트엔드워밍업스터디클럽워밍업스터디클럽리액트

징니

인프런 워밍업 클럽 3기 BE 스터디 2주차

💻 강의입문자를 위한 Spring Boot with Kotlin - 나만의 포트폴리오 사이트 만들기 📚 학습@Profile프로필이 default일 때만 DataInitializer 클래스를 생성해 빈으로 등록@Component @Profile(value = ["default"]) class DataInitializer { } kotlin-loggingprintln()을 사용하지 않고, log를 사용하고 싶어서 따로 찾아보고 적용했다kotlin-logging 방식을 적용했고, Kakao Pay 기술 블로그가 도움이 됐다최신 버전Kakao Pay 기술 블로그private val logger = KotlinLogging.logger { } @Component @Profile(value = ["default"]) class DataInitializer(...) { @PostConstruct fun initializeData() { logger.info { "테스트 데이터 초기화" } ...2025-03-14T04:50:10.942+09:00 INFO 12988 --- [ main] c.j.portfolio.domain.DataInitializer : 테스트 데이터 초기화Repository 테스트 코드@DataJpaTest : JPA 관련 테스트를 위한 설정을 제공@TestInstance : 테스트 인스턴스의 라이프사이클을 지정  TestInstance 부분이 이해가 잘 안돼서 따로 찾아보니 이해할 수 있었다@TestInstance 참고Fetch JoinJoin을 활용해 한 번에 부모와 자식 데이터를 조회할 수 있지만 OneToMany, ManyToMany 관계의 자식 Entity가 여러 개일 경우, 하나만 조인할 수 있다는 한계가 있다@Query("select e from Experience e join fetch e.details where e.isActive = :isActive") fun findAllByIsActive(isActive: Boolean): List<Experience>// Fetch Join 적용 전 SIZE = 5 2025-03-16T03:40:41.646+09:00 INFO 2212 --- [ main] c.j.p.d.r.ExperienceRepositoryTest : findAllByIsActive 테스트 시작 Hibernate: select e1_0.id, e1_0.created_date_time, e1_0.description, e1_0.end_month, e1_0.end_year, e1_0.is_active, e1_0.start_month, e1_0.start_year, e1_0.title, e1_0.updated_date_time from experience e1_0 where e1_0.is_active=? 2025-03-16T03:40:41.721+09:00 INFO 2212 --- [ main] c.j.p.d.r.ExperienceRepositoryTest : experiences.size: 5 Hibernate: select d1_0.experience_id, d1_0.id, d1_0.content, d1_0.created_date_time, d1_0.is_active, d1_0.updated_date_time from experience_detail d1_0 where d1_0.experience_id=? 2025-03-16T03:40:41.729+09:00 INFO 2212 --- [ main] c.j.p.d.r.ExperienceRepositoryTest : experience.details.size: 1 Hibernate: select d1_0.experience_id, d1_0.id, d1_0.content, d1_0.created_date_time, d1_0.is_active, d1_0.updated_date_time from experience_detail d1_0 where d1_0.experience_id=? 2025-03-16T03:40:41.731+09:00 INFO 2212 --- [ main] c.j.p.d.r.ExperienceRepositoryTest : experience.details.size: 2 Hibernate: select d1_0.experience_id, d1_0.id, d1_0.content, d1_0.created_date_time, d1_0.is_active, d1_0.updated_date_time from experience_detail d1_0 where d1_0.experience_id=? 2025-03-16T03:40:41.734+09:00 INFO 2212 --- [ main] c.j.p.d.r.ExperienceRepositoryTest : experience.details.size: 3 Hibernate: select d1_0.experience_id, d1_0.id, d1_0.content, d1_0.created_date_time, d1_0.is_active, d1_0.updated_date_time from experience_detail d1_0 where d1_0.experience_id=? 2025-03-16T03:40:41.736+09:00 INFO 2212 --- [ main] c.j.p.d.r.ExperienceRepositoryTest : experience.details.size: 4 Hibernate: select d1_0.experience_id, d1_0.id, d1_0.content, d1_0.created_date_time, d1_0.is_active, d1_0.updated_date_time from experience_detail d1_0 where d1_0.experience_id=? 2025-03-16T03:40:41.741+09:00 INFO 2212 --- [ main] c.j.p.d.r.ExperienceRepositoryTest : experience.details.size: 5 2025-03-16T03:40:41.742+09:00 INFO 2212 --- [ main] c.j.p.d.r.ExperienceRepositoryTest : findAllByIsActive 테스트 종료// Fetch Join 적용 후 SIZE = 5 2025-03-16T03:43:37.319+09:00 INFO 9488 --- [ main] c.j.p.d.r.ExperienceRepositoryTest : findAllByIsActive 테스트 시작 Hibernate: select e1_0.id, e1_0.created_date_time, e1_0.description, d1_0.experience_id, d1_0.id, d1_0.content, d1_0.created_date_time, d1_0.is_active, d1_0.updated_date_time, e1_0.end_month, e1_0.end_year, e1_0.is_active, e1_0.start_month, e1_0.start_year, e1_0.title, e1_0.updated_date_time from experience e1_0 join experience_detail d1_0 on e1_0.id=d1_0.experience_id where e1_0.is_active=? 2025-03-16T03:43:37.369+09:00 INFO 9488 --- [ main] c.j.p.d.r.ExperienceRepositoryTest : experiences.size: 5 2025-03-16T03:43:37.372+09:00 INFO 9488 --- [ main] c.j.p.d.r.ExperienceRepositoryTest : experience.details.size: 1 2025-03-16T03:43:37.374+09:00 INFO 9488 --- [ main] c.j.p.d.r.ExperienceRepositoryTest : experience.details.size: 2 2025-03-16T03:43:37.375+09:00 INFO 9488 --- [ main] c.j.p.d.r.ExperienceRepositoryTest : experience.details.size: 3 2025-03-16T03:43:37.376+09:00 INFO 9488 --- [ main] c.j.p.d.r.ExperienceRepositoryTest : experience.details.size: 4 2025-03-16T03:43:37.376+09:00 INFO 9488 --- [ main] c.j.p.d.r.ExperienceRepositoryTest : experience.details.size: 5 2025-03-16T03:43:37.378+09:00 INFO 9488 --- [ main] c.j.p.d.r.ExperienceRepositoryTest : findAllByIsActive 테스트 종료Batch Fetch SizeIN 절을 사용해 여러 건의 데이터를 한 번에 조회할 수 있지만 한 번에 많은 데이터를 불러오는 것은 애플리케이션이나 데이터베이스에 부담을 줄 수 있기 때문에 적절한 개수 설정이 필요하다// Batch Fetch Size = 10일 경우 ?도 10개 ... project_id in (?,?,?,?,?,?,?,?,?,?) Batch Fetch Size를 적용하기 전에는 detail에 대한 쿼리가 매번 실행됐지만, 적용 후에는 한 번만 실행된다Size는 5인데 Batch Fetch Size를 3으로 두면 detail에 대한 쿼리는 두 번만 실행된다IN 절에 최대 3개까지만 포함pring: jpa: properties: hibernate: default_batch_fetch_size: 10// Batch Fetch Size 적용 전 SIZE = 5 2025-03-17T16:53:55.480+09:00 INFO 4356 --- [ main] c.j.p.d.r.ProjectRepositoryTest : findAllByIsActive 테스트 시작 Hibernate: select p1_0.id,p1_0.created_date_time,p1_0.description,p1_0.end_month,p1_0.end_year,p1_0.is_active,p1_0.name,s1_0.project_id,s1_0.id,s1_0.created_date_time,s1_0.skill_id,s2_0.id,s2_0.created_date_time,s2_0.is_active,s2_0.name,s2_0.skill_type,s2_0.updated_date_time,s1_0.updated_date_time,p1_0.start_month,p1_0.start_year,p1_0.updated_date_time from project p1_0 left join project_skill s1_0 on p1_0.id=s1_0.project_id join skill s2_0 on s2_0.id=s1_0.skill_id where p1_0.is_active=? 2025-03-17T16:53:55.565+09:00 INFO 4356 --- [ main] c.j.p.d.r.ProjectRepositoryTest : projects.size: 5 Hibernate: select d1_0.project_id,d1_0.id,d1_0.content,d1_0.created_date_time,d1_0.is_active,d1_0.updated_date_time,d1_0.url from project_detail d1_0 where d1_0.project_id=? 2025-03-17T16:53:55.573+09:00 INFO 4356 --- [ main] c.j.p.d.r.ProjectRepositoryTest : project.details.size: 1 2025-03-17T16:53:55.574+09:00 INFO 4356 --- [ main] c.j.p.d.r.ProjectRepositoryTest : project.skills.size: 1 Hibernate: select d1_0.project_id,d1_0.id,d1_0.content,d1_0.created_date_time,d1_0.is_active,d1_0.updated_date_time,d1_0.url from project_detail d1_0 where d1_0.project_id=? 2025-03-17T16:53:55.577+09:00 INFO 4356 --- [ main] c.j.p.d.r.ProjectRepositoryTest : project.details.size: 2 2025-03-17T16:53:55.577+09:00 INFO 4356 --- [ main] c.j.p.d.r.ProjectRepositoryTest : project.skills.size: 2 Hibernate: select d1_0.project_id,d1_0.id,d1_0.content,d1_0.created_date_time,d1_0.is_active,d1_0.updated_date_time,d1_0.url from project_detail d1_0 where d1_0.project_id=? 2025-03-17T16:53:55.579+09:00 INFO 4356 --- [ main] c.j.p.d.r.ProjectRepositoryTest : project.details.size: 3 2025-03-17T16:53:55.582+09:00 INFO 4356 --- [ main] c.j.p.d.r.ProjectRepositoryTest : project.skills.size: 3 Hibernate: select d1_0.project_id,d1_0.id,d1_0.content,d1_0.created_date_time,d1_0.is_active,d1_0.updated_date_time,d1_0.url from project_detail d1_0 where d1_0.project_id=? 2025-03-17T16:53:55.588+09:00 INFO 4356 --- [ main] c.j.p.d.r.ProjectRepositoryTest : project.details.size: 4 2025-03-17T16:53:55.589+09:00 INFO 4356 --- [ main] c.j.p.d.r.ProjectRepositoryTest : project.skills.size: 4 Hibernate: select d1_0.project_id,d1_0.id,d1_0.content,d1_0.created_date_time,d1_0.is_active,d1_0.updated_date_time,d1_0.url from project_detail d1_0 where d1_0.project_id=? 2025-03-17T16:53:55.604+09:00 INFO 4356 --- [ main] c.j.p.d.r.ProjectRepositoryTest : project.details.size: 5 2025-03-17T16:53:55.606+09:00 INFO 4356 --- [ main] c.j.p.d.r.ProjectRepositoryTest : project.skills.size: 5 2025-03-17T16:53:55.607+09:00 INFO 4356 --- [ main] c.j.p.d.r.ProjectRepositoryTest : findAllByIsActive 테스트 종료// Batch Fetch Size 적용 후 SIZE = 5 2025-03-17T16:59:17.289+09:00 INFO 19160 --- [ main] c.j.p.d.r.ProjectRepositoryTest : findAllByIsActive 테스트 시작 Hibernate: select p1_0.id,p1_0.created_date_time,p1_0.description,p1_0.end_month,p1_0.end_year,p1_0.is_active,p1_0.name,s1_0.project_id,s1_0.id,s1_0.created_date_time,s1_0.skill_id,s2_0.id,s2_0.created_date_time,s2_0.is_active,s2_0.name,s2_0.skill_type,s2_0.updated_date_time,s1_0.updated_date_time,p1_0.start_month,p1_0.start_year,p1_0.updated_date_time from project p1_0 left join project_skill s1_0 on p1_0.id=s1_0.project_id join skill s2_0 on s2_0.id=s1_0.skill_id where p1_0.is_active=? 2025-03-17T16:59:17.369+09:00 INFO 19160 --- [ main] c.j.p.d.r.ProjectRepositoryTest : projects.size: 5 Hibernate: select d1_0.project_id,d1_0.id,d1_0.content,d1_0.created_date_time,d1_0.is_active,d1_0.updated_date_time,d1_0.url from project_detail d1_0 where d1_0.project_id in (?,?,?,?,?,?,?,?,?,?) 2025-03-17T16:59:17.385+09:00 INFO 19160 --- [ main] c.j.p.d.r.ProjectRepositoryTest : project.details.size: 1 2025-03-17T16:59:17.386+09:00 INFO 19160 --- [ main] c.j.p.d.r.ProjectRepositoryTest : project.skills.size: 1 2025-03-17T16:59:17.387+09:00 INFO 19160 --- [ main] c.j.p.d.r.ProjectRepositoryTest : project.details.size: 2 2025-03-17T16:59:17.387+09:00 INFO 19160 --- [ main] c.j.p.d.r.ProjectRepositoryTest : project.skills.size: 2 2025-03-17T16:59:17.387+09:00 INFO 19160 --- [ main] c.j.p.d.r.ProjectRepositoryTest : project.details.size: 3 2025-03-17T16:59:17.387+09:00 INFO 19160 --- [ main] c.j.p.d.r.ProjectRepositoryTest : project.skills.size: 3 2025-03-17T16:59:17.388+09:00 INFO 19160 --- [ main] c.j.p.d.r.ProjectRepositoryTest : project.details.size: 4 2025-03-17T16:59:17.388+09:00 INFO 19160 --- [ main] c.j.p.d.r.ProjectRepositoryTest : project.skills.size: 4 2025-03-17T16:59:17.390+09:00 INFO 19160 --- [ main] c.j.p.d.r.ProjectRepositoryTest : project.details.size: 5 2025-03-17T16:59:17.390+09:00 INFO 19160 --- [ main] c.j.p.d.r.ProjectRepositoryTest : project.skills.size: 5 2025-03-17T16:59:17.391+09:00 INFO 19160 --- [ main] c.j.p.d.r.ProjectRepositoryTest : findAllByIsActive 테스트 종료// Batch Fetch Size = 3, SIZE = 5 2025-03-17T17:09:35.183+09:00 INFO 944 --- [ main] c.j.p.d.r.ProjectRepositoryTest : findAllByIsActive 테스트 시작 Hibernate: select p1_0.id,p1_0.created_date_time,p1_0.description,p1_0.end_month,p1_0.end_year,p1_0.is_active,p1_0.name,s1_0.project_id,s1_0.id,s1_0.created_date_time,s1_0.skill_id,s2_0.id,s2_0.created_date_time,s2_0.is_active,s2_0.name,s2_0.skill_type,s2_0.updated_date_time,s1_0.updated_date_time,p1_0.start_month,p1_0.start_year,p1_0.updated_date_time from project p1_0 left join project_skill s1_0 on p1_0.id=s1_0.project_id join skill s2_0 on s2_0.id=s1_0.skill_id where p1_0.is_active=? 2025-03-17T17:09:35.245+09:00 INFO 944 --- [ main] c.j.p.d.r.ProjectRepositoryTest : projects.size: 5 Hibernate: select d1_0.project_id,d1_0.id,d1_0.content,d1_0.created_date_time,d1_0.is_active,d1_0.updated_date_time,d1_0.url from project_detail d1_0 where d1_0.project_id in (?,?,?) 2025-03-17T17:09:35.259+09:00 INFO 944 --- [ main] c.j.p.d.r.ProjectRepositoryTest : project.details.size: 1 2025-03-17T17:09:35.260+09:00 INFO 944 --- [ main] c.j.p.d.r.ProjectRepositoryTest : project.skills.size: 1 2025-03-17T17:09:35.261+09:00 INFO 944 --- [ main] c.j.p.d.r.ProjectRepositoryTest : project.details.size: 2 2025-03-17T17:09:35.261+09:00 INFO 944 --- [ main] c.j.p.d.r.ProjectRepositoryTest : project.skills.size: 2 2025-03-17T17:09:35.261+09:00 INFO 944 --- [ main] c.j.p.d.r.ProjectRepositoryTest : project.details.size: 3 2025-03-17T17:09:35.261+09:00 INFO 944 --- [ main] c.j.p.d.r.ProjectRepositoryTest : project.skills.size: 3 Hibernate: select d1_0.project_id,d1_0.id,d1_0.content,d1_0.created_date_time,d1_0.is_active,d1_0.updated_date_time,d1_0.url from project_detail d1_0 where d1_0.project_id in (?,?,?) 2025-03-17T17:09:35.264+09:00 INFO 944 --- [ main] c.j.p.d.r.ProjectRepositoryTest : project.details.size: 4 2025-03-17T17:09:35.265+09:00 INFO 944 --- [ main] c.j.p.d.r.ProjectRepositoryTest : project.skills.size: 4 2025-03-17T17:09:35.265+09:00 INFO 944 --- [ main] c.j.p.d.r.ProjectRepositoryTest : project.details.size: 5 2025-03-17T17:09:35.265+09:00 INFO 944 --- [ main] c.j.p.d.r.ProjectRepositoryTest : project.skills.size: 5 2025-03-17T17:09:35.266+09:00 INFO 944 --- [ main] c.j.p.d.r.ProjectRepositoryTest : findAllByIsActive 테스트 종료아쉬운 점금요일부터 이번 주 강의를 듣게 됐는데 생각보다 오래 걸려서 섹션 4까지 강의를 다 못 들은 게 아쉽다 오래 걸린 이유늦게 듣기 시작강의를 들으면서 부족한 내용 구글링 및 보완Trouble Shooting 내용 정리 다음 주는 진도가 뒤처지지 않도록 매일 들어야겠다보완할 점아직은 Kotlin으로 Project를 하는 것이 낯설고 어렵게 느껴진다Kotlin 문법에 얼른 익숙해지도록 공부해야겠다강의 매일 듣기Kotlin 문법 공부Kotlin Spring Project 찾아보기혼자 테스트 코드 작성해보기Trouble Shooting/h2-console 접속 오류findById 최적화 궁금증 해결회고테스트 코드를 작성하는 건 역시 어려운 것 같다Java Spring Project를 했을 때도 테스트 코드를 작성하는 것은 어려웠다일단 이번 주에 작성했던 Repository 테스트 코드를 다시 살펴보고, 테스트 코드를 잘 작성하려면 어떻게 공부를 해야 하는지 질문을 남겨봐야겠다이번 주는 유독 어려웠던 주차였지만, 동시에 좀 더 성장할 수 있었던 주차였다스터디 수료 후에는 테스트 코드와 친해져 있었으면 좋겠다  🎯 미션 2테이블 설계하기ERD, 표 등 테이블 설계한 내용을 readme 파일에 작성커밋 메시지 : [미션2] 테이블 설계하기미션2 제출 스레드에 깃허브 커밋 링크를 공유문제학사 관리 서비스를 주제로 선택했을 때, 학생이 여러 과목을 수강 신청할 수 있는 기능만 CRUD로 구성해 가볍게 미니 프로젝트를 진행하려고 했다하지만 수강 신청 기능만 봤을 때 U 부분을 어떻게 해야 할지 많이 고민 됐다C : 수강 신청R : 수강 신청 목록 조회D : 수강 신청 취소결국, 처음 의도와 달리 기능이 확장되면서 테이블도 증가하게 되었다그리고 설계를 하면서 각 테이블 컬럼도 증가하게 되었다2T → 4T해결 도전해결은 아니지만, 설계한 대로 진행해 보려고 한다Kotlin은 처음이지만, Spring Project는 처음이 아니기 때문에 Kotlin을 사용해 학사 관리 서비스의 기능 구현을 마무리하는 것을 이번 미니 프로젝트의 목표로 삼았다회고처음 주제를 선택했을 때, 기능 구현에 대해 단순하게 생각했다그 결과, 테이블을 설계할 때 처음 의도와는 다른 구조가 만들어졌다주제에 따른 기능을 충분히 검토하고, 기한 내에 구현할 수 있는 기능인지 고민했어야 했는데, 한편으로는 자만했던 부분도 있었다Kotlin 문법만 금방 익히면, Spring 프로젝트는 처음이 아니었기 때문에 추후 기능이 확장되더라도 문제없을 거라고 안심했던 것 같다  🎯 미션 3REST API 설계하기API를 설계한 내용을 readme 파일에 작성한 뒤 커밋커밋 메시지 : [미션3] REST API 설계하기미션3 제출 스레드에 깃허브 커밋 링크를 공유문제세부 기능이 정리가 안돼 설계할 때 수정을 자주 했다해결권한별로 기능을 분리하니 이전보다 명확해져 세부 기능을 정리하기가 쉬웠다덕분에 URL을 설정하는 것도 훨씬 수월해졌다공통 기능학생 기능prefix: /students교수 기능prefix: /professors관리자 기능prefix: /admins 회고보통 도메인별로 기능을 정리해 설계하는 것 같고, 나도 항상 그렇게 해왔다하지만 이번 서비스에서는 각 권한에 따른 역할이 뚜렷하기 때문에 권한에 따른 URL 경로를 설정하는 방식으로 하였고, 권한별로 분리하니 각 기능이 직관적으로 보였다 서비스가 커지면 권한에 따라 기능이 세분화되어 URL 경로가 복잡해질 수 있고, 오히려 마이너스가 될 수 있다고 생각했다주요 기능이 소수이되 규모가 작은 미니 프로젝트인 경우에만 개인적으로 사용해야겠다

백엔드

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

미션2. 고객 조사 설계하기 조사 대상 서비스:가상의 음악 스트리밍 서비스 '일일추천음악'무엇을 개선하기 위해 조사를 하려고 하는가:'일일추천음악'의 추천 시스템의 사용성와 추천 방식을 개선하기 위함임 인터뷰를 통해 얻고자 하는 내용:- '일일추천음악' 유저들의 사용 경험을 조사하고, 조사한 사용경험을 바탕으로 '일일 추천 음악'의 추천 알고리즘을 개선하고자 함 인터뷰 방식:1. 심층 인터뷰 (10명)- '일일 추천 음악'을 매일 접속하고, 4시간 이상 음악을 듣는 유저들을 대상으로 인터뷰를 진행함2. 정성 인터뷰 (1000명)- '일일 추천 음악'을 3개월 이상 구독하고 있는 유저들에게 랜덤하게 팝업 형태로 설문조사 페이지를 띄워, 조사를 진행함 인터뷰 질문: 정성 인터뷰 - '일일 추천 음악' 서비스를 언제부터 이용하셨나요?- '일일추천음악' 서비스는 어떤 때 이용하시나요?- 타 경쟁 서비스에 비해서 '일일추천음악'은 어떤 강점을 지니고 있나요? - 반대로 '일일추천음악'에서 보완해야 하는 사항이 있을까요? 심층 인터뷰 (정성 인터뷰와 동일한 질문으로 진행)- '일일추천음악' 추천 시스템을 얼마나 사용하고 계신가요?- (적게 이용하고 있다 라고 말한다면,) 추천 시스템을 이용하지 않는 이유가 있으신가요? - (많이 이용하고 있다 라고 말한다면,) 추천 시스템을 이용하시면서 불편하신 점이 있으셨나요?

강준혁

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

✍ 블로그 요약: 읽기 좋은 코드 & 테스트 코드 정리📌 Readable Code: 가독성 높은 코드 작성법1⃣ 주석 최소화주석이 많으면 코드가 비즈니스 요구사항을 제대로 반영하지 못한 것.코드로 표현할 수 없는 의사 결정 기록만 남기기.2⃣ 변수 & 메서드 정렬 원칙변수: 사용 순서대로 배치.메서드: 공개 메서드 → 상태 변경 → 판별 → 조회 순으로 정렬.3⃣ 코드 리팩토링 & 가독성 향상Enum 활용해 의미 전달 (ex: GameStatus.IN_PROGRESS).객체 역할 분리 (ex: GameBoard = 비즈니스 로직, MineSweeper = 컨트롤러).4⃣ 패키지 구조 개선패키지는 문맥을 제공하는 역할.유지보수를 고려해 적절한 수준으로 나누고 팀원과 협의.5⃣ IDE 도구 활용코드 자동 정렬 (Option + Command + L).코드 품질 유지 도구 (SonarCube, lint, .editorconfig).6⃣ 오버 엔지니어링 방지불필요한 추상화/인터페이스 남발 금지.하나뿐인 구현체가 있다면 인터페이스 생략 가능.7⃣ 실무에서 코드 품질 vs 빠른 개발완벽한 코드보다 실용적인 코드가 중요.TODO 주석을 활용해 미래 리팩토링 가이드 남기기.8⃣ 결론: 완벽한 코드는 없다클린 코드도 적절한 수준에서 적용.도메인 지식을 늘리고 비즈니스 요구사항을 이해하는 것이 핵심.📌 Practical Testing: 테스트 코드의 중요성1⃣ 왜 테스트 코드가 중요한가?자동화된 테스트로 빠르고 정확한 피드백 제공.수동 테스트는 비효율적이며 오류 가능성이 높음.2⃣ JUnit5 & AssertJ 활용한 자동화 테스트✅ assertThat을 활용해 검증 코드 작성 → 일관성과 유지보수 용이.3⃣ 테스트 케이스 설계경계값 테스트 등 다양한 시나리오를 고려한 테스트 설계 필요.🔥 미션 & 한주 회고📍 StudyCafe 리팩토링 미션만족스러운 코드 작성 못함 → 마감 기한보다 늦게 제출.피드백을 통해 개선점 발견 → 다시 리팩토링하여 업데이트 예정.📍 한주 회고읽기 좋은 코드 강의 종료 → 테스트 코드 학습 시작.이전 회사에서 테스트 코드를 작성할 기회가 없었음 → 이번 기회에 제대로 배우기!Day 11 미션에서는 코드 리뷰까지 도전!🚀 목표: 실무에서도 활용할 수 있는 코드 품질 & 테스트 코드 역량 기르기!

이지민

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

강의 내용 요약운영체제프로세스간 통신프로세스는 독립적으로 실행되는 경우도 있지만 다른 프로세스와 데이터를 주고 받으며 통신을 하는 경우도 있음프로세스는 한 컴퓨터 내에서 실행 중인 다른 프로세스와 통신하거나 네트워크로 연결된 다른 컴퓨터에 있는 프로세스와 통신할 수도 있음. 프로세스 간 통신의 종류한 컴퓨터 내에서 프로세스 간 통신 파일 : 파일을 이용해 데이터를 주고 받으며 통신파이프 : 운영체제가 제공한 파이프를 통해 데이터를 주고 받으며 통신 2. 하나의 프로세스 내에서 쓰레드를 이용한 통신쓰레드는 코드, 데이터, 힙영역을 공유하고 스택만 각자의 것을 가지고 있음데이터 영역의 전역 변수 or 힙을 이용하면 쓰레드 간 통신이 가능3. 원격의 컴퓨터의 프로세스 간 통신(네트워크 이용)운영체제가 제공하는 소켓통신4. 다른 컴퓨터에 있는 함수를 호출하는 RPC(Remote Procedure call : 원격 프로시저 호출)  공유자원과 임계구역1. 공유자원과 동기화 문제프로세스/쓰레드 간 통신을 할 때 여러 프로세스/쓰레드가 공동으로 이용하는 자원(메모리, 변수, 파일, 데이터 등…)을 공유자원이라함공유자원은 여러 프로세스/쓰레드가 공유하고 있기 때문에 프로세스/쓰레드가 공유자원에 접근하는 순서에 따라 실행 결과가 달라질 수 있음시분할처리, 멀티스레드 환경 등의 이유로 인해 프로세스/쓰레드의 실행 순서를 파악하기 어려움참고 사항운영체제는 프로세스의 실행 순서를 관리 및 결정하지만, 실행 결과에는 관여하지 않는다. → 실행만 하고 결과는 알 바가 아니다따라서 공유 자원에 대한 접근 순서가 올바르게 조정되지 않으면 예상치 못한 결과가 발생함이 문제를 동기화 문제 라 부름동기화 문제는 공유 자원에 대한 접근 순서가 잘못된 경우, 공유 자원에 동시 접근 등 공유 자원 사용과 관련된 모든 문제를 아우르는 개념 2. 동기화 문제의 예시// 물약을 먹는 코드 int currentHealth = getHealth(); health = currentHealth + 50; // 체력 50 회복 // 공격받는 코드 int currentHealth = getHealth(); health = currentHealth - 10; // 체력 10 감소 공격받는 코드가 먼저 실행된다고 가정공격받는 코드의 int currentHealth = getHealth(); 가 실행되어 currentHealth에 20이 저장컨텍스트 스위칭이 발생 → 물약먹는 코드가 실행int currentHealth = getHealth();가 실행되어 currentHealth에 20이 저장되고 health = currentHealth + 50; 가 실행되어 health를 70으로 변경다시 컨텍스트 스위칭이 발생 → 공격받는 코드가 실행health = currentHealth - 10; 가 실행되어 health를 10으로 변경물약먹는 코드가 먼저 실행된다고 가정물약먹는 코드의 int currentHealth = getHealth();가 실행되어 currentHealth에 20이 저장컨텍스트 스위칭이 발생 → 공격받는 코드가 실행int currentHealth = getHealth();가 실행되어 currentHealth에 20이 저장되고 health = currentHealth - 10;가 실행되어 health를 10으로 변경다시 컨텍스트 스위칭이 발생 → 물약먹는 코드가 실행health = currentHealth +50가 실행되어 health를 70으로 변경예상 결과값으로는 health가 60이지만 health라는 공유자원에 여러 프로세스가 잘못된 순서로 접근하여 엉뚱한 값이 되어버림 3. 임계구역(Critical Section)과 경쟁 조건(Race Conditon)여러 프로세스/쓰레드가 동시에 사용하면 안되는 영역을 임계 구역이라 함공유자원을 수정하는 코드 부분이며 한 번에 하나의 프로세스/쓰레드만 접근해야 하는 영역여러 프로세스/스레드가 동일한 공유 자원을 동시에 접근해 문제가 발생할 수 있는 상황을 경쟁 조건(Race Condition)이라고 부름경쟁 조건은 동기화 문제의 한 가지 경우에 해당 4. 상호 배제(Mutual Exclusion) 매커니즘임계 구역 문제를 해결하기 위한 원칙상호 배제의 요구사항임계구역에는 동시에 하나의 프로세스만 접근해야 한다여러 개의 요청에도 하나의 프로세스의 접근만 허용한다임계구역에 들어간 프로세스는 최대한 빠르게 나와야한다  세마포어(Semaphore)1. 세마포어의 개념직원 A와 직원 B가 프린터 이용을 하고 싶음프린터 이용 시 작업물이 섞이는 것을 방지하기 위해 프린터실을 따로 만들고 프린트 작업을 원할 때마다 프린터실 열쇠 관리자에게 단 하나의 열쇠를 받아서 프린터실을 이용해야 함한 직원이 프린터실에 들어가있는 동안에는 다른 직원은 절대 프린터실에 출입할 수 없으며 들어간 직원이 나올 때까지 대기해야 함직원 A가 프린터 작업을 마치고 프린터실에서 나와서 열쇠를 열쇠 관리자에게 반납함 프린터를 사용하려는 직원 : 프로세스프린터: 여러 프로세스/쓰레드가 같이 쓰고 있는 공유자원프린터를 쓰기 위해 대기하는 공간: 대기큐프린터실 열쇠 관리자: 운영체제열쇠: 세마포어 2. 세마포어를 코드에 적용하기// 세마포어 선언 int semaphore = 1; // 물약을 먹는 코드 wait(semaphore); //열쇠를 요청해서 열쇠를 받고 문을 잠금 int currentHealth = getHealth(); health = currentHealth + 50; // 체력 50 증가 signal(semaphore); // 방에서 나와 열쇠 관리자에게 열쇠 반납 // 공격받는 코드 wait(sempahore); //열쇠를 요청해서 열쇠를 받고 문을 잠금 int currentHealth = getHealth(); health = currentHealth - 10; // 체력 10 감소 signal(semaphore); // 방에서 나와 열쇠 관리자에게 열쇠 반납 물약을 먹는 코드가 먼저 실행세마포어 변수 semaphore를 가지고 wait() 함수 호출 → **wait()함수**는 세마포어(열쇠)를 획득할 때까지 기다렸다가 획득하면 방에 들어가 문을 잠구는 함수현재 세마포어(열쇠)가 있으니 받아서 문을 잠굼공격받는 코드로 컨텍스트 스위칭이 발생공격받는 코드에서 wait()함수를 호출했지만 세마포어(열쇠)가 없으므로 세마포어(열쇠)를 받을 때까지 대기 → 다음 코드가 실행되지 않고 멈춰있음시간이 지나 다시 물약을 먹는 코드로 컨텍스트 스위칭이 발생signal() 함수가 호출될 때까지 물약을 먹는 코드가 실행물약을 먹는 코드가 signal() 함수를 호출하여 세마포어(열쇠)를 반납하였고 그제서야 공격받는 코드의 wait()함수가 종료됨시간이 지나 다시 공격받는 코드로 컨텍스트 스위칭이 발생하면 health 값을 변경하고 signal() 함수로 세마포어(열쇠)를 반납함초기 health값을 20이라고 하면 health는 20 → 70 → 60으로 변화위 설명을 통해 세마포어를 이용하면 여러 프로세스/쓰레드가 공유자원에 동시에 접근하지 못하기 때문에 동기화 문제가 발생하지 않음세마포어는 여러 개의 값을 가질 수 있음공유자원의 개수에 따라 세마포어의 값을 다르게 조정 가능ex) 공유자원이 3개 → 세마포어의 값은 3 3. 세마포어의 단점wait(s); // 임계 구역 wait(s); wait(s); // 임계 구역 signal(s); signal(s); // 임계 구역 wait(s); 위 그림과 같이 wait()과 signal()의 순서를 이상하게 호출하여 세마포어를 잘못 사용할 가능성이 있음.반납을 안해서 다른 프로세스가 임계 구역에 접근이 불가능하다 등..위 문제는 모니터라는 기법을 통해 해결 가능 4. 모니터세마포어의 단점을 해결한 상호배제 메커니즘운영체제가 아닌 프로그래밍 언어 차원에서 지원하는 방법ex) 자바의 synchronizedpublic class Health{ private int health = 100; synchronized void increase(int amount){ health += amount } synchronized void decrease(int amount){ health -= amount } } // synchornized 키워드가 붙은 함수들은 동시에 여러 프로세스/쓰레드에서 실행 불가능 // ex)(동일한 객체에 속한) increase 함수가 프로세스/쓰레드 A에서 실행 중이면 // 프로세스/쓰레드 B에서는 increase, decrease 모두 실행 불가능   데드락(Deadlock)1. 교착상태(데드락)이란?여러 프로세스/쓰레드가 서로 다른 프로세스/쓰레드의 작업이 끝나기를 기다리다가 아무도 작업을 진행하지 못하는 상태일상생활 속 교착 상태 → 교차로에서의 교통 마비 상태교착 상태의 발생 이유는 공유자원 때문임어떤 자원을 여러 프로세스/쓰레드가 공유하지 않는다면 교착상태는 발생하지 않음 2. 식사하는 철학자원형으로 된 탁자에 음식이 준비되어 있고 철학자들은 각자 의자에 앉아서 음식을 먹는 상황각 철학자는 포크를 1개씩 가지고 있으며 음식을 먹기 위해서는 포크를 2개 사용해야 함철학자 3명이 동시에 자신의 오른쪽에 있는 포크를 집음모든 철학자가 포크가 1개 더 필요한 상황이지만 아무도 양보를 하지 않음아무도 식사가 불가능한 교착상태에 빠짐 3. 교착상태의 필요조건아래의 4가지 필요조건 중 하나라도 충족되지 않는다면 교착상태는 발생하지 않는다.상호 배제어떤 프로세스/쓰레드가 한 리소스를 점유했다면 그 리소스를 사용하는 동안 다른 프로세스에게 공유되면 안됨 식사하는 철학자에서 포크가 리소스에 해당먼저 포크를 집었다면 그 포크는 다른 사람이 사용할 수 없는 리소스 비선점프로세스/쓰레드 A가 리소스를 점유하고 있는데 프로세스/쓰레드 B가 그 리소스를 빼앗을 수 없어야 함식사하는 철학자에서 철학자 A가 들고 있는 포크를 철학자 B가 뺏을 수 없는 상황 점유와 대기어떤 프로세스/쓰레드가 리소스를 가지고 있는 상태에서 추가적인 리소스를 원하지만 추가 리소스를 사용할 수 없어 대기하는 상태식사하는 철학자에서 오른쪽 포크를 손에 쥔 채로 왼쪽 포크를 기다리는 상태 원형 대기점유와 대기를 하는 프로세스/쓰레들의 관계가 원형(순환 구조)을 이루며, 각 프로세스가 다음 프로세스가 보유한 리소스를 기다리는 상황. 식사하는 철학자에서 서로가 서로의 포크를 원하는 상황이 원형(순환구조)을 이룸 4. 교착상태의 예방(Prevention)아예 교착상태 자체가 일어나지 않도록 방지하는 방법운영체제 연구자들은 앞의 4가지 필요조건을 통해 교착상태를 예방하려고 했으나 제약이 많고 비효율적이기 때문에 예방이 아닌 교착상태를 해결하는 방법을 연구하기 시작함 5. 교착상태 회피(Avoidance)운영체제가 프로세스들에게 자원을 할당할 때 어느 정도의 자원을 할당해야 교착상태가 발생하는지 파악하여 교착상태가 발생하지 않는 수준의 자원을 할당하는 방식교착상태가 발생할 수 있지만 최대한 발생하지 않도록 미리 대비하는 방법교착상태 회피는 전체 자원의 수와 할당된 자원의 수를 기준으로 안정상태와 불안정 상태로 나눔운영체제는 최대한 안정 상태를 유지하는 방향으로 자원을 할당함불안정 상태에 있더라도 무조건 교착상태에 빠지는 것이 아니라 교착상태에 빠질 확률이 높아지는 것은행원 알고리즘(Banker’s Algorithm)은행(운영체제)이 사업가들(프로세스/쓰레드)에게 돈(리소스)을 빌려줄 때 은행의 여윳돈(시스템의 총 자원)과 사업가들에게 빌려준 돈들(현재 할당된 자원)을 보고 대출 가능한 상황(안전상태)인지 확인하고 빌려주는 원리의 알고리즘 안정 상태안정상태의 경우 P2 혹은 P3이 추가 자원을 요청할 경우 사용 가능한 자원 2개를 사용하여 할당해줄 수 있음.추가 자원이 할당된 P2 혹은 P3의 작업이 끝나면 자원을 반납받아 P1의 추가 자원 요청에도 대응할 수 있음 불안정 상태현재 운영체제가 사용 가능한 자원이 1개임이 자원으로는 P1, P2, P3가 요청할 수 있는 최대 요청인 2개를 충족하지 못함불안정 상태에 있더라도 프로세스가 추가로 최대 자원을 요청하지 않는다면 교착상태에 빠지지 않을 수도 있지만 안정 상태에 비해 확률이 높음은행원 알고리즘은 교착상태를 회피하는 좋은 방법이지만 비용이 비싸고 비효율적5. 교착상태 검출가벼운 교착 상태 검출타이머를 이용하는 방식 → 프로세스가 일정시간 동안 작업을 진행하지 않는다면 교착상태가 발생했다고 간주하고 교착상태를 해결(프로세스 종료)일정 시점마다 체크포인트를 만들어 작업을 저장하고 타임아웃으로 교착상태가 발생했다면 마지막으로 저장된 체크포인트로 롤백무거운 교착 상태 검출자원할당 그래프를 이용하는 방식 → 현재 운영체제에서 프로세스가 어떤 자원을 사용하는지 지켜보고 교착상태가 발생했다면 해결(프로세스 종료) 프로세스는 각자 자원을 차지하고 있고 화살표를 통해 추가로 다른 자원을 요청하고 있음왼쪽 그래프 : 순환 구조가 생기지 않음 → 교착상태가 없는 그래프오른쪽 그래프: 순환 구조가 발생 → 교착상태가 있는 그래프그래프를 통해 교착 상태를 검출했다면 교착상태를 일으킨 프로세스를 강제종료시키고 다시 실행될 때 체크포인트로 롤백시킴이 방식은 운영체제가 지속적으로 자원 할당 그래프를 유지하고 검사해야 하므로 큰 오버헤드가 발생하지만 가벼운 교착 상태보다 정확한 교착상태 검출 가능(타이머에 의한 강제 종료가 아니라 그래프를 근거로 판단하기 때문)  컴파일과 프로세스1. 프로그래밍 언어의 종류컴파일 언어작성한 코드를 컴파일이라는 과정을 거쳐 0과 1로 이루어진 기계어로 실행파일을 만드는 언어컴파일 과정에서 문법 오류를 검사하고 CPU에서 바로 처리 가능한 기계어로 실행파일을 만들어두기 때문에 속도가 빠름번역가가 원고를 읽고 통째로 번역한 다음 전달해주는 느낌C, C++, C# 등의 언어가 이에 해당인터프리터 언어작성한 코드를 미리 기계어로 만들지 않고 실행 시 코드를 1줄씩 해석해 실행하는 언어컴파일 과정이 없기 때문에 오류가 발생할 수 있고 컴파일 언어에 비해 속도도 느림동시통역사가 중간에서 즉석으로 통역해주는 느낌Javascript, Python, Ruby등의 언어가 이에 해당 2. 컴파일 과정가장 먼저 전처리기에서 전처리 과정이 진행됨전처리 구문이 처리됨전처리 구문: #으로 시작하는 구문#include, #define …코드에 있는 모든 주석은 제거됨printf()함수의 원형을 가져옴매크로 값인 MY_NUMBER가 100으로 치환 됨 컴파일러는 C언어 작성된 파일(.i)을 기계어에 가까운 어셈블리어로 변환시킴 어셈블리어로 변환된 파일은 어셈블러를 통해 오브젝트파일(.o)파일로 변환됨오브젝트 파일은 0과 1로 된 기계어로 구성되어 있기 때문에 일반적인 텍스트 에디터로는 내용을 확인할 수 없음오브젝트 파일은 코드 영역과 데이터 영역이 분리되어 있음 링커는 여러 개의 오브젝트 파일을 하나의 코드영역과 데이터영역으로 묶음실제로 실행될 주소를 매핑시켜줌링커까지 거치면 실행파일(.exe)파일이 생성됨 사용자가 프로그램을 실행시키면 운영체제가 프로세스를 생성운영체제는 exe파일에 있는 코드영역과 데이터영역을 가져와 프로세스의 코드영역과 데이터영역에 넣어주고 빈 상태의 스택과 힙을 할당PCB를 만들어 프로세스 관리가 가능하도록 만듦PCB의 프로그램 카운터를 생성한 프로세스의 코드영역의 첫번째 주소로 설정운영체제의 CPU 스케줄링에 따라 프로세스가 실행되다가 작업을 마치고 종료됨  메모리1. 메모리 종류레지스터가장 빠른 기억장소로 CPU내에 존재전원이 공급되지 않으면 데이터가 사라지는 휘발성 메모리32bit 크기의 레지스터를 가짐 → 32bit CPU64bit 크기의 레지스터를 가짐 → 64bit CPUcpu는 계산을 할 때 메인메모리에 있는 값을 레지스터로 가져와서 계산계산결과는 다시 메인메모리에 저장캐시레지스터와 메모리의 중간 다리 역할을 하는 메모리레지스터가 필요로 할 것 같은 데이터를 메인메모리에서 미리 가져와서 저장해두는 장소휘발성 메모리캐시는 성능의 이유로 L1, L2, L3등 여러 개가 존재CPU가 값을 요청해 레지스터로 값을 옮겨야 할 때 가장 속도가 빠른 L1 → L2 → L3 → 메인메모리의 순서대로 참조하여 값을 가져옴 (값이 없으면 하위 단계를 참조하는 방식)메인메모리실제 운영체제와 다른 프로세스들이 올라가는 공간전원이 공급되지 않으면 데이터가 사라지는 휘발성 메모리보조저장장치보다 가격이 비싸고 휘발성이기 때문에 실행중인 프로그램만 올림보조저장장치(HDD/SSD)가격이 저렴하고 전원이 공급되지 않아도 데이터가 지워지지 않는 비휘발성 메모리 2. 메모리와 주소유니프로그래밍 환경에서는 하나의 프로세스만 메모리에 올라오기 때문에 메모리 관리가 어렵지 않았음멀티프로그래밍 환경에서는 여러 프로세스가 메모리에 올라오기 때문에 메모리 복잡하고 어려워졌음주소 변환 필요프로세스 간 메모리 침범 방지 대책 필요동적 메모리 할당 및 해제가 복잡프로세스 간 메모리 공유 및 보호 문제32bit CPU와 64bit CPU32bit CPU레지스터 크기, ALU, 버스의 크기가 모두 32bit최대 메모리의 크기: 2^32 = 4GB64bit CPU레지스터 크기, ALU, 버스의 크기가 모두 64bit최대 메모리의 크기: 2^64 = 거의 무한대64bit CPU가 1번에 처리할 수 있는 양이 더 많으므로 속도가 더 빠름물리주소와 논리주소 물리 주소 공간: 메모리의 실제 주소공간논리 주소 공간: 사용자 관점에서 바라본 주소공간 경계 레지스터사용자 프로세스가 운영체제의 영역에 함부로 접근하지 못하도록 하드웨어적으로 운영체제 공간과 사용자 공간을 나누는 역할을 하는 레지스터CPU내에 존재하는 레지스터로 메모리 관리자가 사용자 프로세스가 경계 레지스터의 값을 벗어났는지 검사하고 만약 벗어났다면 그 프로세스를 종료시킴 절대주소와 상대주소 실제 프로그램은 4000번지에서 시작되지만 컴파일러는 0번지에서 시작한다고 생각함절대 주소(물리 주소)메모리 내에서 프로세스가 실행되는 실제 주소 공간메모리 관리자 관점으로 바라본 주소그림에서는 실제 프로그램이 올라간 4000번지상대 주소(논리주소)사용자 관점으로 바라본 주소그림에서는 컴파일러가 생각한 0번지 CPU가 100번지의 값을 요청하면 재배치 레지스터 저장된 값인 4000번지의 값을 더한 4100번지의 값에 접근하여 데이터를 가져옴재배치 레지스터에는 프로그램의 시작주소가 저장되어 있음메모리 관리자는 사용자 접근 시마다 위의 방식대로 계산메모리 관리자 덕분에 주소에 대해 크게 신경쓰지 않아도 프로그램을 만들 수 있고 시작영역이 변경되더라도 재배치 레지스터의 값만 변경하면 되기에 굉장히 유연함 3. 메모리 할당방식메모리 오버레이메모리의 크기보다 더 큰 프로그램을 실행시키는 방법큰 프로그램을 작게 나누어 일부만 메모리에서 실행하고 나머지는 하드디스크의 스왑영역에 저장 하지만 스왑영역도 하드디스크의 일부 영역이고 스왑영역의 데이터와 메모리의 데이터를 교체하는 작업이 필요하므로 실제 메모리보다는 느리게 동작함가변 분할 방식과 고정 분할 방식 한 프로세스가 메모리의 연속된 공간에 할당되므로 연속 메모리 할당 이라고도 부름 한 프로세스가 메모리에 분산되어 할당되기 때문에 비연속 메모리 할당 이라고도 부름가변 분할 방식의 장/단점장점 단점 프로세스의 크기에 딱 맞게 할당됨 → 내부 단편화가 없음 외부 단편화가 발생고정 분할 방식의 장/단점장점 단점 같은 크기로 나누기 때문에 단순함 → 구현이 간단하고 오버헤드가 적음 작은 프로세스가 큰 공간에 할당되어 낭비되는 공간이 발생 → 내부 단편화가 발생외부 단편화충분한 메모리 공간이 있음에도, 연속된 빈 공간의 크기가 부족하여 프로세스를 메모리에 할당할 수 없는 상태 조각 모음을 통해 외부 단편화가 발생한 공간을 합칠 수 있지만 실행중인 프로세스들을 일시 중지해야하고 메모리 공간을 이동시키는 작업을 수행해야 하므로 오버헤드 발생내부 단편화분할 메모리의 크기가 프로세스가 필요한 크기보다 크게 할당되어 내부에 빈 공간이 생겨 낭비되는 현상 낭비되는 공간을 해결할 뚜렷한 방법은 없으나, 분할 메모리의 크기를 조절하여 내부단편화를 최소화하는 전략을 취함버디 시스템2의 제곱수로 메모리를 분할하여 메모리를 할당하는 방식 메모리를 2의 제곱수로 나누어 프로세스가 필요로 하는 크기보다 작을 때까지 나눔위 그림에서는 500이므로 256까지 메모리를 나눔나눴을 때 가장 작은 단위보다 한 단계 위의 공간에 프로세스를 할당256에는 할당할 수 없으므로 512에 할당여기서도 내부 단편화가 발생하지만 그 값을 최소화할 수 있음프로세스가 종료되어 메모리가 다시 확보되었을 때 비슷한 크기 혹은 같은 크기의 분할 공간이기에 메모리를 다시 합치기도 용이함 버디 시스템 방식가변 분할 방식처럼 프로세스의 크기에 따라 유동적으로 메모리를 할당 가능외부 단편화 방지를 위해 메모리 공간을 확보하는 것이 용이외부고정 분할 방식처럼 내부 단편화가 발생하지만 많은 공간 낭비가 발생하지 않음 자료구조재귀(Recursion)1. 재귀란?어떠한 것을 정의할 때 자기 자신을 참조하는 것재귀적으로 정의된 함수를 재귀함수라고 부름 2. 재귀 함수의 간단한 예시// 잘못된 방식의 재귀함수 function myFunction(number){ console.log(number); myFunction(number + 1); } myFunction(1); // 이 함수를 실행하면 정확한 종료 조건(기저 조건)이 없기 때문에 // 콜스택이 쌓여 계속 메모리 공간이 가득 차서 자동으로 종료됨 // 올바른 형식의 재귀함수 function myFunction(number){ if (number > 3) return; console.log(number); myFunction(number + 1); } myFunction(1); // 이 함수는 1 ~ 3까지는 재귀적으로 자신을 호출하다가 // 종료 조건(기저 조건)인 number > 3에 걸리게 되면 함수를 종료 3. 콜스택스택의 영역의 일부 중 함수가 호출되면서 올라가는 영역재귀 함수 호출 시 플로우 차트 위 그림처럼 함수를 호출할 때마다 위와 같이 콜스택에 쌓이게 됨재귀를 사용하는 이유 : 더 복잡한 문제를 쉽게 해결하기 위해팩토리얼, 피보나치 수열문제 등 4. 재귀적으로 생각하기 (재귀로 풀 수 있는 유형 알아보기)단순 반복 → 반복문을 재귀로 변경하기반복문을 재귀로 변경하면 성능 상 이점이 크게 없음 (메모리 문제 때문에 오히려 감소할 수도…)하위 문제의 결과를 상위 문제의 해결에 사용하는 경우 (하향식 계산) ⭐⭐⭐배열의 합, 문자열의 길이, power 함수 … 5. 하노이의 탑 ⭐⭐⭐하향식 접근을 통한 하노이의 탑 풀이하노이의 탑 코드 /** * @param {*} ringCount 원반 개수 * @param {*} from 출발지 기둥 * @param {*} to 임시 기둥 * @param {*} temp 목적지 기둥 * @returns */ function hanoi(ringCount, from, to, temp) { /* 종료(기저) 조건 */ if (ringCount == 0) return; /* 재귀 조건 */ // 기둥 A에 있는 원반 1,2를 B로 옮김 hanoi(ringCount - 1, from, temp, to); // 기둥 A에 있는 원반 3을 C로 옮김 console.log(`원반 ${ringCount}을(를) ${from}에서 ${to}로 이동`); // 기둥 B에 있는 원반 1,2을 C로 옮김 hanoi(ringCount - 1, temp, to, from); } hanoi(3, 'A', 'C', 'B'); 일주일 간의 회고🍷칭찬하고 싶은 점절대적 시간이 부족했지만 포기하지 않고 운영체제 강의는 끝까지 완주한 점배운 내용까지 최대한 디테일하게 정리한 점😅아쉬웠던 점자료구조 및 알고리즘의 재귀까지는 제대로 강의를 들었지만 정렬 부분은 아직 정리를 못했습니다. ㅠㅠㅠ예비군 2박3일 일정을 알고 있었는데도 시간 관리를 제대로 하지 못한 점이 아쉽습니다.🛠보완하고 싶은 점시간에 쫒겨서 제대로 정리하지 못한 부분을 제대로 정리하고 마지막 주차 잘 준비했으면 합니다.일단 이해가 가지 않더라도 중간에 끊지 않고 먼저 1번 듣고 그 다음에 정리하는 습관을 몸에 들여야겠습니다.

커리어 · 자기계발 기타

szun

워밍업 클럽 스터디 3기(PM) - [미션 2] 고객 조사 설계하기

프로덕트 배경최근 IT 직무에 대한 인기가 점점 높아지고 있으며, 비전공자들 또한 IT 직무로 전환을 희망하는 경우가 증가하고 있음.이러한 비전공자들의 경우 IT 직무에 대한 경험을 쌓는 가장 좋은 방법은 사이드 프로젝트에 참여하여 개발 사이클을 경험해 보는 것임.현재 대다수의 팀 빌딩 방식은 다음과 같음.팀을 모집하고자 하는 주체가 모집글을 작성하고 다양한 플랫폼에 게시참여를 희망하는 불특정 다수는 모집글을 보고 지원지원자를 받고 선별하여 팀을 확정기존의 팀 빌딩 방식이 다소 불편하다고 생각하여 다른 방식을 도입하고자 하는 것이 목적.프로덕트 개요IT 직무 희망자 및 현업 종사자에게 자신의 경력과 보유 기술을 간편하게 보여줄 수 있는 플랫폼을 제공프로젝트 팀 빌더에게는 원하는 팀원을 직접 컨택할 수 있도록 하여, 맞춤형 팀 빌딩을 실현할 수 있는 가치를 제공고객 조사 계획1. 조사 주제기존 사이드 프로젝트 팀 빌딩 방식의 불편함이 실제로 존재하는가?불편함이 실제로 존재한다면 나의 가설과 일치하는가존재하지만 내가 생각한 것과는 다른 불편함인가2. 조사 대상자 선정IT 직군 취업 준비생IT 직군으로 전환을 희망하는 비전공자IT 직군과 관련된 전공을 수료 중인 대학생사이드 프로젝트 경험이 있는 사람3. 조사 방법1 대 1 심층 인터뷰정성 데이터 획득을 위한 수단구글 폼을 이용한 간단한 설문조사정량 데이터 획득을 위한 수단4. 인터뷰 질문 설계기존 방식의 이용 경험 파악그동안 사이드 프로젝트 팀원을 구하거나 합류하기 위해 어떤 과정을 거치셨나요?이 과정에서 어려움을 느꼈던 적이 있다면 구체적으로 어떤 부분이 가장 힘드셨나요?팀을 꾸릴 때 사람들이 지원을 해도 실제로 프로젝트로 연결이 잘 되지 않았던 이유가 있나요?이미 프로젝트가 구성된 팀에 합류해본 경험이 있으시다면, 지원 과정을 통해 느낀 불편함도 있나요?구체적인 불편함 & 이유 탐색불편함을 느꼈던 이유가 무엇이라고 생각하시나요?이런 불편함이나 문제 때문에 프로젝트 자체를 포기하거나, 사람을 구하는 걸 중단한 적도 있으신가요?불편함을 해소하기 위해 직접 시도했던 방법이나 우회로가 있으신가요?기존 방식에 대한 생각 & 대안지금 사용하시는 방식이 만족스럽지 않았다면, 가장 큰 이유는 무엇인가요?기존 방식이 불편함에도 불구하고 어쩔 수 없이 쓰게 되는 이유가 있다면 무엇일까요?인터뷰 질문지 설계인터뷰 목적기존 방식(모집글을 올리고 사람들이 지원하는 방식)의 실제 이용 경험을 듣고,그 과정에서 느낀 불편함, 어려움, 불만족을 구체적으로 파악하기 위함.인터뷰 질문 내용소개 및 아이스브레이킹자기소개 관련 질문(이름/나이/직업 등)사이드 프로젝트를 진행해본 경험이 있으신가요?팀 빌딩 시 주로 어떤 플랫폼이나 방법을 사용하셨나요?기존 방식 이용 경험 파악그동안 사이드 프로젝트 팀원을 구하거나 합류하기 위해 어떤 과정을 거치셨나요?이 과정에서 어려움을 느꼈던 적이 있다면 구체적으로 어떤 부분이 가장 힘드셨나요?팀을 꾸릴 때 사람들이 지원을 해도 실제로 프로젝트로 연결이 잘 되지 않았던 이유가 있나요?이미 프로젝트가 구성된 팀에 합류해본 경험이 있으시다면, 지원 과정을 통해 느낀 불편함도 있나요?구체적인 불편함 & 이유 탐색불편함을 느꼈던 이유가 무엇이라고 생각하시나요?이런 불편함이나 문제 때문에 프로젝트 자체를 포기하거나, 사람을 구하는 걸 중단한 적도 있으신가요?불편함을 해소하기 위해 직접 시도했던 방법이나 우회로가 있으신가요?기존 방식에 대한 생각 & 대안지금 사용하시는 방식이 만족스럽지 않았다면, 가장 큰 이유는 무엇인가요?기존 방식이 불편함에도 불구하고 어쩔 수 없이 쓰게 되는 이유가 있다면 무엇일까요?마무리또다른 경험 or 불편함에 대한 질문기타 개선 희망 사항에 대한 질문감사 인사

기획 · PM· PO

인프런 워밍업 스터디 클럽 3기 (FE) _ 2주차 발자국

강의를 학습하며 새로 배운 점2주차 학습 범위 :따라하며 배우는 자바스크립트 A-Z 8~10섹션, 따라하며 배우는 React A-Z 2~6섹션이번주는 리액트가 진도에 포함됐다.완전히 처음하는 것은 아니지만 기존에 그냥 리액트가 뭔지 맛보기 수준으로 해보았기에많이 복잡하고 어려웠다. 강의 내용자체는 들으면서 이해는 되지만 막상 내가 프로젝트를 만드려고 하면 문제가 생겼다.특히 tailwindcss를 사용하려고 할 때는 설치하고 지우고 설치하고 지우고를 몇번 반복했는데도 동작하지 않았다...경로도 바꿔보고 혹시 터미널 문제인가 싶어서 ubuntu도 설치해보고 했는데 소용없었다 ㅠㅠ(이전엔 맥북으로 해서 zsh를 썼을때는 프레임워크나 등등을 설치할때 문제가 된 적이 거의 없었고, 윈도우에서 bash를 사용하니 npx 명령어 부터 작동하지 않아서 혹시 싶었다..)리액트를 배우면서 과제까지 해보려고 하니 너무 시간도 부족하고 어려워서 이번주는 배우는 것에만 집중하려고 했다그래서 다음주에 심기일전하고 다시 과제를 해보려고 한다.과제 진행 회고2주차 과제비밀번호 생성 앱 만들기타이핑 테스트 앱 만들기 디즈니 플러스 앱 만들기포켓몬 도감 앱 만들기이번주는 리액트 과제를 포기했다... 다음주에 열심히 해보려고 한다. 비밀번호 생성 앱 만들기가장 여유있을때 한 과제인 만큼 최대한 배운걸 활용해보려고 했다. 객체에 선택지들에 맞는 글자를 넣어두고 ex) 숫자 = "0123456789"랜덤으로 선택된 선택지를 선정 (filter 사용), 선택지 안에 있는 문자열을 인덱스로 접근해서 랜덤하게 하나를 가져왔다그리고 입력한 비밀번호 길이에 맞게 반복해서 비밀번호를 만들었다.filter를 깊게 공부하면서 동적 접근이라는 것도 배웠다 처음엔 이해가 안됐지만 (사실 지금도)사용해보니 훨씬 코드를 단축할 수 있었다.  타이핑 테스트 앱 만들기랜덤 영어 명언 api를 불러 와서 진행했다. (https://api.adviceslip.com/advice)어렸을때 한컴 타자연습을 많이, 그리고 재미있게 했어서 앱에 어떤 기능을 넣어야 하는지는 명확했는데타속이라는 걸 어떤 기준으로 해야하는지는 어려워서 다른 사람들이 한 것들을 많이 검색해보며 참고했다 

rjf1138

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

강의 수강 내용 핵심 정리1. 클린 코드와 추상화클린 코드의 핵심은 가독성 → 코드가 잘 읽히면 이해하기 쉽고 유지보수가 용이함.추상화(Abstract): 구체적인 정보에서 핵심 개념만 뽑아 단순화하는 과정.적절한 추상화는 복잡한 로직과 데이터를 단순화하여 읽기 좋은 코드를 만든다.잘못된 추상화는 문맥이 맞지 않는 용어 사용이나 과도한 단순화로 혼란을 유발할 수 있음.추상화 레벨을 맞추는 것이 중요 → 동일한 코드 블록 내에서는 비슷한 추상화 수준 유지.2. 이름 짓기(Naming)의도를 명확히 전달하는 이름을 사용 (e.g. input, input2 → cellInput, userActionInput).줄임말은 가독성을 해칠 수 있음 → 되도록 풀네임 사용 (e.g. cnt 대신 count).일관된 도메인 용어 사용 → 내부 팀에서 정한 용어를 지키기.단수/복수 명확히 구분 (e.g. row vs rows).getter/setter 사용 최소화 → 객체에 메시지를 보내도록 유도 (isAgeGreaterThan(19)).3. 메서드 설계와 선언부메서드는 하나의 책임(기능)만 가지도록 설계 → 한 가지 이상의 일을 하면 분리.메서드명은 추상적 의미를 담아야 함 (예: processPayment 대신 deductBalance).파라미터와 반환 타입을 신중하게 결정 → void 대신 반환값을 활용하여 테스트 가능하도록 함.메서드의 Depth(중첩 수준) 최소화 → 지나치게 깊은 if문, for문은 리팩토링 필요.4. 가독성을 위한 코드 스타일Early Return 사용 → 불필요한 else 제거로 가독성 향상.공백 라인 활용 → 의미 있는 단위로 코드 분리.부정문 지양 → !isLeft() 대신 isRight() 또는 isNotLeft().매직 넘버 & 매직 스트링 제거 → 10 대신 BOARD_SIZE 사용.변수는 사용되는 곳 가까이 선언 → 코드 이해도를 높이고 유지보수 용이.5. 객체지향 설계 원칙 (SOLID)(1) 단일 책임 원칙 (SRP)한 클래스는 하나의 책임(변경 이유)만 가져야 함.예) 게임 로직과 사용자 입력 처리를 분리 (e.g. GameLogic, UserInputHandler).(2) 개방-폐쇄 원칙 (OCP)기존 코드 수정 없이 기능 확장이 가능해야 함.추상화(인터페이스) 사용으로 변경 사항이 최소화되도록 설계.(3) 리스코프 치환 원칙 (LSP)부모 클래스를 자식 클래스로 대체해도 문제가 없어야 함.자식 클래스에서 부모 클래스의 기능을 임의로 변경하거나 지원하지 않는 기능을 추가하면 LSP 위반.(4) 인터페이스 분리 원칙 (ISP)하나의 인터페이스가 너무 많은 기능을 포함하면 안 됨 → 기능 단위로 분리.예) GameInitializable, GameRunnable로 인터페이스 분리.(5) 의존성 역전 원칙 (DIP)상위 모듈(비즈니스 로직)은 하위 모듈(구현)과 직접적으로 의존하지 않아야 함.인터페이스를 통해 의존성을 주입(DI) → 런타임에 유연한 변경 가능.스프링의 IoC 컨테이너를 활용하면 DIP 자동 적용 가능.6. 예외 처리와 NULL 다루기예외를 의도적으로 구분 → 사용자 예외(AppException) vs 시스템 예외.NULL 사용 최소화 → Optional<T> 활용, orElseGet()으로 성능 최적화.예외 발생 가능성이 높은 구간은 외부 세계와의 접점 (e.g. 입력값, API 응답).7. 코드 리팩토링 접근법처음부터 SOLID 원칙을 완벽하게 적용하려 하지 말고, 도메인 이해를 우선.코드를 작성하면서 점진적으로 리팩토링 → 적절한 추상화와 역할 분리를 고민하며 개선.성능, 유지보수성을 고려하여 때로는 원칙을 트레이드오프할 수도 있음.8. 상속보다는 조합을 사용하자상속은 부모-자식 간 결합도가 높아 수정이 어렵고 유연성이 떨어짐.부모 클래스 변경 시 모든 자식 클래스에 영향을 미침.조합(Composition)과 인터페이스를 활용하면 유연한 구조를 만들 수 있음.코드 중복 제거보다 유연한 설계가 더 중요하다.9. Value Object(VO)와 EntityVO(Value Object): 도메인의 개념을 표현하는 값 객체로, 불변성을 가지며 식별자가 없음.불변성: final 필드 사용, setter 금지.동등성: 값이 같으면 동일한 객체로 취급 → equals() & hashCode() 재정의 필요.유효성 검증: 객체 생성 시점에서 검증 수행.Entity: 식별자가 존재하며, 같은 ID를 가지면 같은 객체로 취급.VO와 Entity의 차이:Entity: 시간이 지나면서 값이 변할 수 있음.VO: 생성된 이후 값이 변하지 않으며, 모든 값이 같아야 같은 객체로 취급됨.→ equals() & hashCode()를 재정의해야 하는 이유와, hash 자료형을 구현하는 방법을 학습할 필요가 있음.10. 일급 컬렉션(First-Class Collection)컬렉션을 단순히 사용하지 않고 객체로 포장하여 의미를 부여함.컬렉션을 감싸면서 로직을 함께 관리 → 가공 로직을 포함하여 유지보수성을 높임.새로운 컬렉션을 반환해야 하는 경우기존 컬렉션을 변경할 여지를 없애기 위해 새로운 리스트(ArrayList 등)를 반환해야 할 때가 있음.예: new ArrayList<>(originalList)는 리스트 객체를 새로 만들지만 내부 요소는 기존 객체를 참조하므로, 원본 데이터를 안전하게 유지하려면 내부 객체도 깊은 복사(Deep Copy)해야 함.11. Enum의 특성과 활용Enum은 단순한 상수가 아닌, 상태와 관련된 로직을 포함할 수 있는 객체.특정 도메인의 개념을 명확하게 표현할 수 있음.변경이 잦은 개념이라면 Enum 대신 DB에서 관리하는 것이 유리함.12. 다형성(Polymorphism) 활용하기반복적인 if-else 문을 줄이기 위해 다형성을 적극 활용.변하는 것은 조건과 행위 → enum + 인터페이스 조합으로 해결 가능.Enum value별로 인터페이스를 구현할 수 있음.한 번 배운 개념도 반복해서 복습하는 것이 중요하다.13. 숨겨진 도메인 개념 도출하기객체 지향은 현실을 100% 반영하는 것이 아니라, 현실을 흉내 내는 것.완벽한 설계는 불가능하며, 그 당시의 최선의 선택이 중요함.시간이 지나면서 틀렸음을 인지할 수도 있기 때문에, 미래 변경 가능성을 고려한 코드 작성이 필요함.미션 해결 과정 정리Day 2 미션우리는 매일 숨을 쉬고 음식을 소화하지만, 이를 매번 세밀하게 설명한다면 너무 복잡하고 불편할 것입니다.이 미션을 수행하면서, 최근 읽고 있는 《데이터 중심 애플리케이션 설계》(a.k.a DDIA)에서 본 문장이 떠올랐습니다."우발적 복잡도(accidental complexity)를 제거하기 위한 최상의 도구는 추상화다."여기서도 추상화라는 개념이 강조됩니다. 또한, 책의 예시에서도 “기계어, CPU 레지스터, 시스템 호출을 추상화한 것이 고수준 프로그래밍 언어이다”라는 설명이 나옵니다. 이는 강의에서 들었던 내용과도 동일합니다.컴퓨터가 실제로 실행하는 것은 어셈블리어와 같은 저수준 코드이지만, 개발자인 우리가 이를 직접 다루는 것은 어렵고 비효율적입니다. 따라서, 우리는 Java 같은 고수준 프로그래밍 언어를 사용하며 자연스럽게 추상화의 이점을 누리고 있는 것입니다.또 다른 추상화의 예시로 떠오른 것은 Spring에서 Database(ex: MySQL)를 사용하기 위한 ORM인 JPA, Spring Data JPA입니다.우리는 데이터베이스를 사용하기 위해 보통 다음과 같은 작업을 수행해야 합니다.Connection Pool에서 Connection을 가져옴데이터베이스와 연결을 맺음SQL 쿼리를 작성하고 실행결과를 받아서 처리이 과정은 데이터베이스를 사용할 때마다 반복되며, 이는 우발적 복잡도를 증가시킵니다. 이를 해결하기 위해 JPA와 Spring Data JPA 같은 ORM이 등장했습니다.즉, 선배 개발자들이 데이터베이스 작업의 복잡성을 추상화하여 보다 간결하고 일관된 방식으로 데이터를 다룰 수 있도록 한 것입니다.또한, Java의 interface 역시 추상화의 대표적인 사례라고 생각합니다.interface는 구현 세부 사항을 숨기고, 다양한 구현체를 유연하게 사용할 수 있도록 하며, 다형성을 활용하여 코드의 재사용성과 유지보수성을 높이는 역할을 합니다.이렇듯, 이번 미션에서는 다시 한번 올바른 추상화의 중요성과 그것이 Readable Code 를 만드는데 얼마나 큰 요소를 차지하는지 잘 알 수 있었습니다.애플리케이션 설계, 코드 구현 등 여러 곳에서 추상이라는 개념이 얼마나 중요하게 여기는지 다시 한번 알 수 있었고, 적재적소에 잘 써먹어야겠다는 생각을 많이 했습니다.Day 4 미션AS-ISpublic 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; } Readable 하지 못한 점 파악if-else 가 중첩되어 있다. → 사고의 depth 를 줄이자!early return 을 하자!부정 연산자(!)의 가독성이 떨어진다.getter 의 연속 사용 → 메서드화 하자!TO-BE@Service @RequiredArgsConstructor public class OrderService { ... public boolean validateOrder(Order order) { if (order.hasNoItems()) { throw new OrderException("주문 항목이 없습니다."); } if (order.hasNotCustomerInfo()) { throw new OrderException("사용자 정보가 없습니다."); } if (order.isInValidTotalPrice())) { throw new OrderException("올바르지 않은 총 가격입니다."); } return true; } } @Entity @Table(name = "orders") @Where(clause = "deleted_at IS NULL") @SQLDelete(sql = "UPDATE orders SET deleted_at = NOW() WHERE id = ?") public class Order { ... private boolean hasNoItems() { return this.items.isEmpty(); } private boolean hasNotCustomerInfo() { return this.customInfo.isEmpty(); } private boolean isTotalPriceLessThanZero() { return this.items.stream().mapToInt(Item::getPrice).sum() <= 0; } } public class OrderException extends RuntimeException { public OrderException(String message) { super(message); } } if-else 중첩문을 if 문 3개로 변환했다.order.getItems().size() == 0 getter 의 연속적 사용을 메서드화하여 Order 객체 안으로 숨겨서 캡슐화했다.!order.hasCustomerInfo() 과 같은 부정 연산자를 없애고, hasNotCustomerInfo 처럼 부정어도 메서드화 하여 가독성을 높였다.추가적으로 if 문 조건에 들어가는 메서드들을 Order class 안으로 옮기고,단순히 log 처리만 하는 것을 넘어서 RuntimeException 인 OrderException 으로 예외처리를 하였다.SOLID에 대하여 자기만의 언어로 정리해 봅시다.SRP : 단일 책임 원칙(single responsibility principle)OCP : 개방-폐쇄 원칙 (Open/closed principle)LSP : 리스코프 치환 원칙 (Liskov substitution principle)ISP : 인터페이스 분리 원칙 (Interface segregation principle)DIP : 의존관계 역전 원칙 (Dependency inversion principle)SRP(단일 책임 원칙)한 클래스는 하나의 책임만 가져야 한다.중요한 기준은 변경이다. 변경이 있을 때 파급 효과가 적으면 단일 책임 원칙을 잘 따른 것OCP(개방-폐쇄 원칙)소프트웨어 요소는 확장에 열려 있으나 변경에는 닫혀 있어야 한다.다형성을 활용해보자인터페이스를 구현한 새로운 클래스를 하나 만들어서 새로운 기능을 구현하면 코드에 변경이 없다.LSP(리스코프 치환 원칙)프로그램의 객체는 프로그램의 정확성을 깨뜨리지 않으면서 하위 타입의 인스턴스로 바꿀 수 있어야 한다.다형성에서 하위 클래스는 인터페이스 규약을 다 지켜야 한다는 것ISP(인터페이스 분리 원칙)특정 클라이언트를 위한 인터페이스 여러 개가 범용 인터페이스 하나보다 낫다덩어리가 크면 그걸 다 구현하기가 힘들다. 덩어리가 작으면 작은 기능만 구현하면 되니까 훨씬 쉬워진다.인터페이스가 명확해지고, 대체 가능성이 높아진다.DIP(의존관계 역전 원칙)프로그래머는 추상화(역할)에 의존해야지, 구체화(구현)에 의존하면 안된다. 의존성 주입은 이 원칙을 따르는 방법 중 하나다.쉽게 이야기해서 구현 클래스에 의존하지 말고, 인터페이스에 의존하라는 뜻앞에서 이야기한 역할(Role)에 의존하게 해야 한다는 것과 같다.객체 세상도 클라이언트가 인터페이스에 의존해야 유연하게 구현체를 변경할 수 있다. 구현체에 의존하게 되면 변경이 아주 어려워진다.개인적으로 Spring Bible 로 생각하는 토비의 스프링, 김영한님의 Spring 강의를 듣고 채득한 내용을 바탕으로 SOLID 개념을 정리해봤습니다.일주일 회고다른 워밍업 클럽과는 달리 한 달 안에 2개의 강의, 그것도 강의당 대략 13시간 정도의 강의를 수강해야 해서 강의를 빨리 들어야 하는 압박감이 들어서 개인적으로는 쉽지 않았던 것 같습니다.그래도, 진도에 늦지 않게 강의를 수강한 것 같아서 무척 뿌듯하네요.그리고, 이번주에는 미션이 2개 있었는데 해당 미션을 수행하면서 강사님이 중요하게 생각하는 건 무엇인지, 강의를 들을 때 어떤 걸 중점적으로 들어야 하는지에 대한 일종의 가이드라인을 주시는 것 같아서 강의를 수강하기에 좀 더 수월 했던 것 같습니다.현재, 2년 2개월을 회사 생활을 마치고, 이직 준비를 한지 벌써 1년이 지나면서 바쁘게 취직 준비를 하고 있지만 그럼에도 박우빈님의 강의를 들으면서 클린코드/테스트코드를 공부하기 정말 잘했다는 소위 말하는 돈값을 한다는 강의를 느꼈습니다.남은 3주도 열심히 하여 완강은 물론 우수러너도 되어보도록 하겠습니다.

rjf1138

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

💡 강의 핵심 내용 정리📝Readable Code1. 주석의 역할과 사용 원칙🚫 주석을 최소화해야 하는 이유주석이 많다는 것은 비즈니스 요구사항을 코드로 잘 표현하지 못한 것을 의미한다.주석에 의존하면 적절한 추상화 수준을 유지하기 어려워 코드 품질이 낮아진다.✅ 언제 주석을 사용해야 할까?코드로 표현할 수 없는 의사 결정의 히스토리를 남길 때.자주 변경되지 않는 정보만 포함하고, 지속적으로 관리해야 한다.부정확한 주석은 차라리 없는 것보다 나쁘다.2. 변수와 메서드 정렬 순서🔍 변수 정렬 원칙변수는 사용하는 순서대로 배치하여 가독성을 높인다.📌 메서드 정렬 원칙공개 메서드 (public) 먼저 배치상태 변경 > 판별 > 조회 순으로 배치.같은 기능을 하는 메서드는 그룹화하여 정렬.**비공개 메서드 (private)**는 공개 메서드에서 호출하는 순서대로 정렬.3. 코드 리팩토링 & 가독성 향상🏗 주석 없이 코드로 의미 전달하기 (Enum 활용)java복사편집// ❌ 기존 코드 private int gameStatus = 0; // 0: 게임 중, 1: 승리, -1: 패배 // ✅ 개선 코드 public enum GameStatus { IN_PROGRESS("진행 중"), WIN("승리"), LOSE("패배"); }🎯 객체 역할 분리GameBoard → 게임 진행 및 로직 담당 (비즈니스 로직)MineSweeper → 사용자 입력/출력 제어 (컨트롤러 역할)4. 패키지 구조 개선📌 패키지는 "문맥"을 제공한다유지보수를 고려하여 적절한 수준으로 패키지를 나눈다.패키지 변경은 팀원과 협의 후 진행하는 것이 좋다.5. IDE 도구 활용⚙ 코드 자동 정렬Option + Command + L (Mac, IntelliJ)🔍 코드 품질 유지 도구SonarCube, lint (ESLint, ktlint).editorconfig를 활용해 일관된 코드 스타일 유지6. 오버 엔지니어링 방지🚫 불필요한 추상화 & 인터페이스 남발구현체가 하나뿐인 인터페이스는 불필요한 경우가 많다.추상화를 도입하면 정보가 숨겨지므로 복잡성이 증가.필요 이상으로 정보를 숨기면 코드 유지보수가 어려워진다.7. 실무에서 코드 품질 vs 빠른 개발⚖ 균형 잡힌 선택이 필요하다완벽한 코드보다 실용적인 코드가 중요하다.클린 코드를 목표로 하되, 현실적인 개발 속도도 고려해야 한다.TODO 주석 등을 활용하여 미래 리팩토링을 위한 가이드 남기기.8. 결론: "완벽한 코드"는 없다클린 코드도 은탄환이 아니다. 적절한 수준에서 적용하는 것이 중요하다.테스트 코드도 잘 관리하지 않으면 오히려 유지보수 부담이 될 수 있다.가장 중요한 것은 도메인 지식을 늘리고, 코드가 어떤 비즈니스 요구사항을 해결하는지 파악하는 것 💻Practical Testing1. 테스트 코드의 필요성🤔 왜 테스트 코드가 중요한가?자동화된 테스트를 통해 빠르고 정확한 피드백 제공.사람이 직접 테스트하면 비효율적이고 실수가 발생할 가능성이 높음.🚫 수동 테스트의 문제점java복사편집@Test void add_manual_test() { CafeKiosk cafeKiosk = new CafeKiosk(); cafeKiosk.add(new Americano()); System.out.println(">>> 담긴 음료 수 : " + cafeKiosk.getBeverages().size()); } ❌ 사람이 직접 확인해야 하므로 자동화되지 않고 오류가 발생할 가능성이 높음.✅ JUnit5 & AssertJ 활용한 자동화 테스트java복사편집@Test void add() { CafeKiosk cafeKiosk = new CafeKiosk(); cafeKiosk.add(new Americano()); assertThat(cafeKiosk.getBeverages()).hasSize(1); assertThat(cafeKiosk.getBeverages().get(0).getName()).isEqualTo("아메리카노"); } ✔ 자동화된 검증을 통해 일관성 유지✔ 테스트 실패 시 바로 원인 파악 가능2. 테스트 케이스 설계🔍 경계값 테스트범위 (이상, 이하, 초과, 미만), 구간, 날짜 등 고려하여 테스트 설계. 💡 미션 Day 7이번주 미션은 StudyCafe 라는 프로젝트(스터디 카페 이용권 구매 서비스)를 리팩토링하는 미션이었다.하지만, 시간이 부족하여 내 자신이 만족할 만한 코드를 작성하지 못하였고, 계속 수정하다가 결국 마감 시간 보다도 늦게 제출하고 말았다...이후에, 중간점검 날 다른 분들의 피드백도 보고 깨달은 바가 많았다. 꼭 다시 새로운 마음으로 리팩토링 하여 마감 기한에 상관 없이 다시 제출해보고, 지금 작성 중인 2주차 발자국에도 내가 어떤 과정 및 생각으로 미션을 수행했는지 업데이트 할 것이다.💡 한주 회고이번 한주도 엄청 바빴다. 읽기 좋은 코드 강의를 마무리하고 새로 테스트 코드 강의로 들어섰다. 이전 회사에서 테스트 코드를 제대로 짜본 적이 없고 또 그럴 환경이 뒷바쳐 주지 못해서 작성하지 못하였다. 그래서, 테스트 코드에 대한 갈증이 더욱 컸다. 이번 기회에 제대로 배우고 시야를 넓혀가야겠다. Day 11 미션에는 꼭 강사님께 코드 리뷰 제출까지 해봐야겠다. 화이팅!!

주이

[워밍업 클럽 3기] CS 2주차 - 운영체제 미션

FIFO 스케줄링의 장단점이 뭔가요? 장점단순하고 직관적단점한 프로세스가 완전히 끝나야 다음 프로세스가 진행되므로, 실행시간이 짧고 늦게 도착한 프로세스가 실행시간이 길고 빨리 도착한 프로세스의 작업을 기다려야 함I/O 작업이 있다면, CPU는 I/O 작업이 끝날 때까지 쉬고있기 때문에 CPU 사용률이 떨어짐 2. SJF를 사용하기 어려운 이유가 뭔가요?어떤 프로세스가 얼마나 실행될지 예측하기 어려움Burst Time이 긴 프로세스는 아주 오래동안 실행되지 않을 수 있음-> SJF는 Burst Time이 짧은 프로세스가 먼저 실행되기 때문에 긴 프로세스는 뒤로 밀려남  3. RR 스케줄링에서 타임 슬라이스가 아주 작으면 어떤 문제가 발생할까요?오버헤드가 너무 커진다. 컨텍스트 스위칭이 자주 일어나게 되고, 타임 슬라이스에서 실행되는 처리량보다 컨텍스트 스위칭을 처리하는 양이 더 많아지기 때문 운영체제가 MLFQ에서 CPU Bound Process와 I/O Bound Process를 어떻게 구분할까요?CPU를 사용하는 프로세스가 실행하다가 스스로 CPU를 반납하면 CPU 사용이 적음 -> I/O Bound Process 확률 증가타임 슬라이스 크기를 오버해서 CPU 스케줄러에 의해 강제로 CPU를 뺏기는 상황 -> CPU Bound Process 확률 증가 공유자원이란 무엇인가요?프로세스 간 통신을 할 때 공동으로 이용하는 변수나 파일 교착상태에 빠질 수 있는 조건은 어떤 것들을 충족해야 할까요?상호 배제: 어떤 프로세스가 한 리소스를 점유했다면, 그 리소스는 다른 프로세스에게 공유되면 안 된다.비선점: 다른 프로세스가 리소스를 뺏을 수 없어야 한다.점유와 대기: 어떤 리소스를 가지고 있을 때, 다른 리소스를 원하는 상태여야 한다. 원형 대기: 점유와 대기를 하는 프로세스들의 관계가 원형을 이룬다. (원하는 리소스의 방향이 원형을 이룸) 

huihui

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

강의 수강Readable Code: 읽기 좋은 코드를 작성하는 사고법 학습 내용 요약섹션2. 추상추상화: 구체에서 정보를 제거하고 함축하여 중요한 것만 남기는 과정구체화: 추상을 보고 유추를 통해 생략된 정보를 재현해내서 이해하는 과정추상화 레벨: 얼마나 추상화했는지를 나타내는 단계고수준: 추상화 레벨이 높다 (추상적)저수준: 추상화 레벨이 낮다 (구체적)적절한 추상화: 해당 도메인의 문맥 안에서 핵심 개념만 남겨서 표현하는 것-> 적절한 추상화는 복잡한 데이터/로직을 단순화하여 코드를 이해하기 쉽게 한다.이름 짓기, 메서드로 추출 + 메서드 선언부, 매직 넘버/매직 스트링 상수로 추출섹션3. 논리, 사고의 흐름뇌 메모리 적게 쓰기 (인지적 경제성)코드를 작성할 때 읽는 사람의 뇌 메모리를 최대한 적게 사용하도록 작성할 것Early returnearly return: 메서드를 분리해 끝낼 수 있는 케이스들은 빨리 return 해버려 아래쪽 케이스를 읽을 때 위쪽 케이스를 신경쓰지 않아도 되도록 하는 것 사고의 depth 줄이기코드를 읽는 사람의 사고가 적당한 수준으로(추상화된 정도로) 이해할 수 있도록 메서드 분리공백 라인으로 의미 단위 표현부정 연산자 제거예외 처리개발자가 의도한 예외개발자가 의도하지 않은 예외섹션4. 객체 지향 패러다임 관심사의 분리: 관심사에 따라 기능과 책임을 나누어 객체 생성높은 응집도낮은 결합도SOLIDSRP(단일 책임 원칙)하나의 클래스는 하나의 책임(관심사)만을 가져야 한다.SRP를 지킴으로써 각 클래스의 응집도를 높이고, 클래스 간의 결합도를 낮출 수 있다.OCP(개방-폐쇄 원칙)확장에는 열려 있고, 변경에는 닫혀 있어야 한다.기존 코드의 변경 없이도 시스템의 기능을 확장할 수 있어야 한다.추상화와 다형성을 활용해서 OCP를 지킬 수 있다.LSP(리스코프 치환 원칙)부모 클래스의 인스턴스를 자식 클래스의 인스턴스로 치환해도 정상 작동해야 한다.상속 구조에서 자식 클래스는 부모 클래스의 책임을 준수하고 행동을 변경하지 않아야 한다.ISP(인터페이스 분리 원칙)클라이언트는 자신이 사용하지 않는 인터페이스에 의존하면 안된다.인터페이스를 필요한 기능 단위로 잘게 쪼개서 사용해야 한다.DIP(의존성 역전 원칙)상위 수준의 모듈은 하위 수준의 모듈에 의존해서는 안 된다.둘 모두 추상화에 의존해야 한다.구체적인 구현 클래스가 아닌 추상화(인터페이스, 추상 클래스)에 의존해야 한다.섹션5. 객체 지향 적용하기  회고평소에 코드를 짤 때 좋은 코드 작성에 대해서 깊게 생각하지 않았다. 일단 코드가 제기능을 하는지가 가장 큰 관심사였고, 개인 프로젝트를 주로 해왔기 때문에 내가 이해할 수 있으면 가독성에 대해 크게 신경쓰지 않았기 때문이다. 그래서 읽는 사람보다는 코드를 작성하는 나의 입장에서 편한 방향으로 코드를 작성해왔다.강의를 왜 좋은 코드를 작성해야 하는지 명확하게 이해하고, 지금껏 생각하지 못한 관점을 배울 수 있었다. 또한 강사님을 따라 코드를 리팩토링하며 앞으로 코드를 짤 때 배운 내용을 적용하여 읽는 이가 이해하기 쉬운 코드를 짤 수 있도록 노력해야 겠다는 다짐을 하게 되었다.칭찬하고 싶은점: 바쁜 한주였지만 매일 학습 진도표에 맞춰서 미루지 않고 강의를 들었다.아쉬웠던 점: 강의 내용을 정리하며 학습하는 것은 조금 밀렸고, 미션 수행만으로는 학습한 내용을 내가 온전히 이해하고 있는지 확인하기 어려웠다.보완할 점: 이론적인 내용을 정리하는 것 외에도 배운 내용을 실제 코드에 적용해보는 연습이 더 필요할 것 같다.다음 주 목표: 강의와 미션 외에도 기존에 내가 짠 코드를 리팩토링 해봐야겠다. 미션코드 리팩토링리팩토링 전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 EMPTY_ORDER_MESSAGE = "주문 항목이 없습니다."; public static final String INVALID_TOTAL_PRICE_MESSAGE = "올바르지 않은 총 가격입니다."; public static final String NO_USER_INFO_MESSAGE = "사용자 정보가 없습니다."; public boolean validateOrder(Order order) { if (order.hasNoItem()) { log.info(EMPTY_ORDER_MESSAGE); return false; } if (order.hasInvalidTotalPrice()) { log.info(INVALID_TOTAL_PRICE_MESSAGE); return false; } if (order.hasNoCustomerInfo()) { log.info(NO_USER_INFO_MESSAGE); return false; } return true; }미션 해결 과정코드를 살펴보며 강의에서 배운 내용을 하나씩 적용해보았다.중첩되어 복잡한 분기문 -> Early return을 적용해 불필요한 else와 중첩된 분기 제거if문의 조건이 복잡함 -> if문의 조건을 메서드로 추출 분기 조건이 한번에 이해되도록 함 (사고의 depth 줄임)또한 order의 정보를 객체 외부에서 getter로 빼와 조작하는 대신 order가 처리하게 하여 캡슐화를if문 조건에 부정 연산자 존재 -> 별도의 메서드(hasNoItem())를 만들어 if문 조건의 부정 연산자(!) 제거매직 스트링이었던 로그 메시지를 상수로 추출해 가독성을 높이고 유지보수를 용이하게 함읽는 이가 의미 단위로 이해할 수 있도록 공백 라인 추가회고if문의 조건을 order의 메서드로 추출하는 과정에서 메서드의 이름을 짓는 것이 생각보다 고민되었다. 좀 더 많은 코드를 보며 이런 기능의 메서드의 이름을 관용적으로 어떻게 짓는지 공부해볼 필요가 있어 보인다.다시 보니 log.info()로 처리되고 있는 예외 처리 부분을 thrwo와 try-catch로 관리하는 것이 유지보수에 좋았을 것 같다는 생각이 든다.

백엔드

huihui

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

강의 수강Readable Code: 읽기 좋은 코드를 작성하는 사고법 학습 내용 요약섹션6. 코드 다듬기좋은 주석주석의 양면성주석은 코드로 표현할 수 있는 내용을 대체하는 것으로, 지양해야 함의사 결정의 히스토리를 코드로 표현할 수 없을 때 주석으로 설명해야 함으로 필수임좋은 주석: 코드를 통해 최대한 표현했음에도 전달하지 못한 정보가 남았을 때 사용하는 주석나열 순서변수: 사용하는 순서대로 나열 (인지적 경제성 고려)객체의 공개/비공개 메서드공개 메서드는 기준(중요도, 종류 등)을 가지고 배치비공개 메서드는 공개 메서드에 언급된 순서대로 나열나열 순서로도 의도와 정보 전달 가능 패키지 나누기패키지는 문맥으로써의 정보를 제공패키지 분리 시 유의할 점적절히 쪼개야 함 (너무 안 쪼개면 관리 어렵고, 너무 쪼개도 유지보수 어려움)공통으로 사용하는 클래스들의 패키지 분리/변경은 충돌을 야기함패키지 구조는 가능한 한 초기 프로젝트 설정 때 잘 고민해서 나누기기능 유지보수하기버그 잡기알고리즘 교체IDE 도움 받기 정렬 단축키: Ctrl + Alt + Llinting, style - sonarlint, editorconfig섹션7. 리팩토링 연습메서드 추출로 추상화 레벨 맞추기Optional객체에 메시지 보내기객체의 책임과 응집도IO 통합일급컬렉션display()Order 추출추상화 관점의 차이구현에 초점을 맞춘 추상화 vs. 도메인 개념에 초점을 맞춘 추상화섹션8. 기억하면 좋은 조언들 능동적 읽기복잡하고 엉망인 코드를 읽고 이해해야 할 때 리팩토링하면서 읽기공백으로 단락 구분메서드와 객체로 추상화이해한 내용 주석으로 표기오버 엔지니어링필요한 적정 수준보다 더 높은 수준의 엔지니어링 (불필요)적절하게 적용해서 리팩토링할 것은탄환은 없다항상 정답인 기술은 없다한계까지 연습해보고, 적정 수준, 적정 시점을 깨닫기섹션3. 단위 테스트단위 테스트작은 코드 단위(클래스 or 메서드)를 독립적으로 검증하는 테스트검증 속도가 빠르고 안정적임수동 테스트, 자동화 테스트수동 테스트: 사람이 검증 (콘솔에 출력)자동화 테스트: 기계가 검증Junit5, AssertJJunit5: 단위 테스트를 위한 테스트 프레임워크AssertJ: 테스트 코드 작성을 돕는 테스트 라이브러리해피 테스트, 예외 케이스경계값 테스트:범위, 구간의 경계값으로 테스트 테스트하기 쉬운/어려운 영역 (순수함수)테스트하기 어려운 영역관측할 때마다 다른 값에 의존하는 코드외부 세계에 영향을 주는 코드 (외부에 의존하는 코드)순수 함수테스트하기 쉬운 코드같은 입력에 항상 같은 결과 출력 lombok@Data, @Setter, @AllArgsConstructor 지양양방향 연관관계 시 @ToString 순환 참조 문제섹션4. TDD: Test Driven DevelopmentTDD (테스트 주도 개발)프로덕션 코드보다 테스트 코드를 먼저 작성 -> 테스트가 구현 과정을 주도하도록 하는 방법론 레드-그린-리팩토링레드: 실패하는 테스트 작성그린: 테스트를 통과하는 최소한의 코딩리팩토링: 구현 코드 개선 및 테스트 통과 유지애자일(Agile) 방법론 vs. 폭포수 방법론익트트림 프로그래밍 (XP, eXtreme Programming) 빠른 개발 주기와 지속적인 피드백을 중심으로 하는 소프트웨어 개발 방법론 스크럼(Scrum), 칸반(kanban)스크럼: 짧은 개발 스프린트를 반복적으로 진행해 빠르게 결과물을 만들고 지속적으로 개선칸반: 지속적인 개선과 작업량 관리를 위해 작업을 시각적으로 표현섹션5. 테스트는 []다.-> 테스트 코드는 문서다프로덕션 기능을 설명하는 테스트 코드 문서다양한 테스트 케이스를 통해 프로덕션 코드를 이해하는 시각과 관점을 보완어느 한 사람이 과거에 경험했던 고민의 결과물을 팀 차원으로 승격시켜 모두의 자산으로 공유@DisplayName - 도메인 정책, 용어를 사용한 명확한 문장메서드명만으로는 테스트의 의도 파악 어려움DisplayName을 섬세하게 (모두가 명확히 알아볼 수 있도록)명사의 나열보다는 문장으로 기술테스트 결과까지 기술도메인 용어를 사용하여 한층 추상화된 내용을 담Given / When / Then - 주어진 환경, 행동, 상태 변화Given: 시나리오 진행에 필요한 모든 준비 과정When: 시나리오 행동 진행Then: 시나리오 진행에 대한 결과 명시, 검증BDDTDD에서 파생된 개발 방법시나리오에 기반한 테스트 케이스 자체에 집중하여 테스트JUnit vs. SpockSpcok: Groovy 언어로 BDD 패턴을 적용해서 테스트 코드를 작성  회고바쁜 한주를 정신없이 보내고 나니 벌써 중간점검을 할 시기라 좀 놀랐다. 이번주는 할일이 많아 강의 진도를 따라가기 급급한 나머지 Day 7 미션은 기한 내에 끝내지 못했다. 강의를 들으며 강사님을 따라할 때는 어렵지 않았는데, 막상 배운 내용을 적용해보려 하니 생각보다 막막하고 오랫동안 고민해야 했다. 아쉽게도 강의를 끝내지 못한 상태로 중간 점검 라이브에 참여했지만, 다른 사람들이 리팩토링한 코드와 그에 대한 강사님의 피드백을 보며 코드를 리팩토링하는 다양한 접근법을 확인할 수 있어서 좋았다. 동시에 내가 얼마나 부족한지 깨닫는 시간이기도 했다... 이번 스터디가 끝나더라도 배운 내용을 다시 한번 정리하고, 코드를 리팩토링하는 것도 꾸준히 연습해야겠다.칭찬하고 싶은점: 바쁜 한주였지만 들어야 하는 강의는 다 들었고, 지난주에 하지 못한 정리도 끝냈다.아쉬웠던 점: 너무 바빠서 이번주 미션은 끝내지 못했다.보완할 점: 다음 주말에 Day 7 미션을 끝내고, 다른 사람들이 리팩토링한 것과 비교하며 공부하기다음 주 목표: 강의와 미션 진도표에 맞춰서 끝내기 

백엔드

이지민

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

자료구조재귀함수에서 기저조건을 만들지 않거나 잘못 설정했을 때 어떤 문제가 발생할 수 있나요?종료 조건을 설정하지 않거나 잘못 설정했을 경우 콜스택의 누적으로 인한 메모리 부족으로 강제 종료되거나 혹은 의도하지 않은 결과값을 얻을 수 있습니다.0부터 입력 n까지 홀수의 합을 더하는 재귀 함수를 만들어보세요.function sumOdd(n){ // 탈출(기저) 조건 : n이 1일 때 if (n == 1){ return 1; } // 재귀 조건 : n이 1보다 큰 홀수일 때 if (n%2 == 1){ return sumOdd(n-2) + n; } else{ return sumOdd(n-1); } } console.log(sumOdd(10)) // 25다음 코드는 매개변수로 주어진 파일 경로(.는 현재 디렉토리)에 있는 하위 모든 파일과 디렉토리를 출력하는 코드입니다. 다음 코드를 재귀 함수를 이용하는 코드로 변경해보세요.변경 이전const fs = require("fs"); // 파일을 이용하는 모듈 const path = require("path"); // 폴더와 파일의 경로를 지정해주는 모듈 function traverseDirectory1(directory){ const stack = [directory]; // 순회해야 할 디렉토리를 저장할 스택 while (stack.length > 0) { // 스택이 빌 때까지 반복 const currentDir = stack.pop(); // 현재 디렉토리 const files = fs.readdirSync(currentDir); // 인자로 주어진 경로의 디렉토리에 있는 파일or디렉토리들 for (const file of files) { // 현재 디렉토리의 모든 파일or디렉토리 순회 const filePath = path.join(currentDir, file); //directory와 file을 하나의 경로로 합쳐줌 const fileStatus= fs.statSync(filePath); // 파일정보 얻기 if (fileStatus.isDirectory()) { // 해당 파일이 디렉토리라면 console.log('디렉토리:', filePath); stack.push(filePath); } else { // 해당 파일이 파일이라면 console.log('파일:', filePath); } } } } traverseDirectory1("."); // 현재 경로의 모든 하위 경로의 파일, 디렉토리 출력 변경 이후const fs = require("fs"); // 파일을 이용하는 모듈 const path = require("path"); // 폴더와 파일의 경로를 지정해주는 모듈 function traverseDirectory2(directory){ const files = fs.readdirSync(directory); for (const file of files) { // 모든 파일 및 디렉토리를 순회 const filePath = path.join(directory, file); // 현재 디렉토리와 파일/디렉토리 이름을 결합하여 전체 경로 생성 const fileStatus = fs.statSync(filePath); // 파일/디렉토리 정보를 가져옴 /* 재귀조건: 해당 경로가 디렉토리인 경우 */ if (fileStatus.isDirectory()) { console.log('디렉토리:', filePath); traverseDirectoryRecursive(filePath); // 재귀 호출 } /* 탈출조건: 해당 경로가 파일인 경우 */ else { console.log('파일:', filePath); } } } traverseDirectory2("."); // 현재 경로의 모든 하위 경로의 파일, 디렉토리 출력

채널톡 아이콘