블로그

또니

[인프런 워밍업 클럽 2기 CS] 1주차 미션

[운영체제]while(true){ wait(1); // 1초 멈춤 bool isActivated = checkSkillActivated(); // 체크 }이 방식은 폴링방식입니다. 1초마다 체크하기 때문에 성능에 좋지 않습니다.이를 해결하기 위한 방식으로 어떤 걸 이용해야 할까요?=> 인터럽트 방식, 스킬이 사용 되었을 경우 응답을 받는 방식으로 이용한다.프로그램과 프로세스가 어떻게 다른가요?=> 프로그램은 .exe와 같은 실행 파일을 의미하고, 이 .exe와 같은 실행 파일을 실행 했을 때 메모리에 올라가 실행 중인 프로그램 이다.멀티프로그래밍과 멀티프로세싱이 어떻게 다른가요?=> 멀티프로그래밍은 메모리에 여러개의 여러 개의 프로세스가 올라가는 것이고, 멀티 프로세싱은 CPU가 여러개의 프로세스를 처리하는 것이다.운영체제는 프로세스를 관리하기 위해서 어떤 것을 사용하나요?=> 운영체제는 PCB에 담긴 프로세스의 정보를 사용하여 프로세스를 관리한다. 프로세스마다 PCB가 생성되고, 프로세스가 종료되면 PCB도 제거 된다. 이 PCB는 자료구조에서 배웠던 연결리스트로 구성되어 있다. 컨텍스트 스위칭이란 뭔가요?=> A 프로그램이 실행되고 있는 상태에서 B 프로그램이 실행 되도록 하는 것을 말한다. 이때 A 프로그램이 멈춘 시점에서 다시 시작 될 수 있도록 PCB A에 A 프로그램의 상태 값을 저장한 뒤, B 프로그램을 실행하기 위해 PCB B에 있는 이전 상태 값으로 CPU 레지스터 값을 설정한다.[자료구조와 알고리즘]여러분은 교실의 학생 정보를 저장하고 열람할 수 있는 관리 프로그램을 개발하려고 합니다. 이 때 여러분이라면 학생의 정보를 저장하기 위한 자료구조를 어떤 걸 선택하실 건가요? 이유를 함께 적어주세요.=> 해시테이블, 학생은 학생 고유의 학번을 가지고 있기 때문에 학번을 Key로 하는 해시 테이블을 사용할 것입니다.해시테이블은 동적으로 크기를 변화시키는데 유연하여 전학을 오고 갈 수 있는 학생의 정보를 자유롭게 삽입 및 삭제가 가능합니다. 또한, O(1)의 시간복잡도를 가지기 때문에 빠르게 학생의 정보를 검색할 수 있기 때문에 해시테이블을 선택하였습니다.여러분은 고객의 주문을 받는 프로그램을 개발하려고 합니다. 주문은 들어온 순서대로 처리됩니다. 이 때 여러분이라면 어떤 자료구조를 선택하실 건가요? 이유를 함께 적어주세요.=> 큐(Queue), 큐는 선입선출로 먼저 들어온 값이 먼저 나가는 특징을 가지고 있습니다. 주문이 들어온 순서대로 처리되는 프로그램을 개발하기 위해선 큐를 선택하는 것이 적합한 자료구조라고 생각합니다.알고리즘 강의 링크 👉그림으로 쉽게 배우는 자료구조와 알고리즘 (기본편)운영체제 강의 링크 👉그림으로 쉽게 배우는 운영체제

알고리즘 · 자료구조CS지식인프런워밍업클럽2기

Groot

<워밍업 클럽 2기 - 백엔드 클린 코드, 테스트 코드> 2주차 발자국

강의 수강수강한 강의: Readable Code: 읽기 좋은 코드를 작성하는 사고법학습 내용 요약이번주는 OOP, SOLID 원칙에 이어서 실제로 적용해보는 연습을 많이했다.특히, 실제 코드를 기반으로 코드를 어떻게 다음고 리팩터링할 수 있는지 현실적인 접근 방안을 많이 배웠다.회고깔끔하게 코드를 만들고 리팩터링을 어떻게 하라는지는 이해됐다. 하지만 이걸 어떻게 실제로 적용할지는 또 다른 문제였다...그래서 그런지 Day 7 미션이 되게 많은 도움이 되었다. 미션 처음에는 방대하진 않지만 여러 클래스로 나눠진 코드를 바로 크게 개선하려고 하니 부담스럽고 어렵게 느껴졌다. 하지만 차근차근 메서드를 추출하고 적용해가다보니 점점 어떻게 구조를 개선할지 보이게 되어서 되게 재밌었다.미션해결 과정위에서 말했듯이, 코드 실제로 리팩터링하는 미션을 받았다. 회고배운 내용을 토대로 차근차근히 작은 부분부터 인지적으로 읽기 쉬운 구조로 고치고 더 뻗어나가서 책임, 역할에 관한 분배를 나누고 메시지를 정제?하며 확장하기 좋은 코드를 만들어갔다. 되게 새로운 경험이었고 앞으로도 실제 코드를 개선할 때 어떻게 할지 감을 잡을 수 있는 좋은 미션이었다. 

백엔드클린코드워밍업2기자바

김진수

인프런 워밍업 클럽 2기 백엔드 - 발자국 1주차

인프런 워밍업 클럽 2기 발자국 1주차1. 한 주의 정리박우빈님의 읽기 좋은 코드를 작성하는 사고법을섹션1 ~ 섹션5까지 들었다.양으로 보면 적지 않았으나, 내용의 큰 어려움은 없어 강의를 보는 것에 부담은 없었다.편하게 보고 생각을 좀 많이 하게 되는 강의이다.한 주간 강의를 복기해보자.✅ 섹션 2. 추상추상에서는 클린 코드를 추구하는 이유와 추상화 과정에 대해 학습했다.추상화를 거치는 사고 과정 그리고, 단어(변수, 메서드 이름, 클래스 이름 etc.,)와 메서드 그 자체를 추상화 시키는 것에 대해 학습했다. ✅ 섹션 3. 논리, 사고의 흐름논리,사고의 흐름 섹션에서는 추상적인 기법 대신에 조금 더 밀접하게 관련이 있는 것들에 대해 학습했다.예를 들어 Early return 이라든 지 indent를 줄이는 등, 개발자가 개발하는 입장에서보다 읽는 입장을 고려하여 사고를 최대한 적게, 그리고 직관적으로 코드를이해할 수 있도록 짜는 방법에 대해 학습했다. ✅ 섹션 4. 객체 지향 패러다임객체 지향 패러다임 섹션은 객체를 설계하고 지속 가능하고 읽기 좋은 객체로 발전해나가는 방법에 대해 학습했다. 기본적으로는 SOLID에 근간을 두어 설계 했으나, 객체 그 자체가 적절한 책임과 역할을 수행할 수 있도록 만드는 것이 중요했다. ✅ 섹션 5. 객체 지향 적용하기섹션5에서는 4에서 만든 객체를 토대로 활용하는 것을 학습했다.상속과 조합VO일급 컬렉션EnumInterface 활용과 같이 조금은 어려울 수 있으나 제일 흥미로운 부분이었다.사실 이 부분을 어떻게 극한까지 활용하느냐가 자바 개발자의 실력 중 하나이지 않을까? 하는 생각도 했다.2. 두 개의 미션이번 주차는 간단한 사고를 하는 미션 한개와 그리고 직접 해보는 미션 한 개가 주어졌다.어떤 방식으로 접근했고 해결 했는 지에 대해 복기하자. ✅ 추상화첫 번째는 추상화 미션이다. 추상 : 부동산 - 자산의 개념으로 추상화 됨 구체 :1. 아파트 - 특정 위치에 있는 주거용 건물로, 가족 단위의 주거 공간을 제공하며, 시장에서 거래되는 금전적인 정량 지표가 있음2. 상업용 빌딩 - 기업이나 상점이 운영되는 공간으로, 임대 수익을 창출할 수 있는 자산3. 토지 : 개발 가능성이 있는 임야... 등 다양한 자산을 추상화하여 묶어서 부동산으로 표기 나는 위와 같이 이 미션을 제출했는데, 일반적인 제출자들과는 좀 다른 방식으로 접근한 것 같다.많은 분들이 추상화 - 구체화에 대해 중점을 두었는데,나는 이걸 인터페이스와 같은 느낌으로 접근하고 해결했다.부동산이라는 인터페이스에 다양한 구현체가 있다. 여기서 일단 한번 추상화가 들어간다.그리고 부동산이라는 것 자체도 말 그대로 업무를 진행하는 장소인 그것이 있고,경제적 가치를 아우르는 부동산이라는 개념이 또 존재한다.여기서 또 한 번의 추상화가 들어간다. 나는 이 개념이 굉장히 추상화 레벨이 높은 고수준의 추상화라고 생각했기에 제출했다. ✅ 리팩토링 & SOLID에 대해 나만의 정리이 부분은 많이 길기에 따로 블로그에 빼서 작성을 했다.그러나 간략히 요약하자면,이 미션도 최대한 주어진 것에 만족하고 그 이상의 것을 덜어내려 노력했다.첫 번째로 섹션3을 토대로하는 배경이 있기에 3에서 다루지 않은 방식들은 시도하지 않았으며,SOLID에 대한 정리는... 나 스스로는 사람마다 정리가 다르다면 원칙이 아니지 않나? 라는 생각으로SOLID를 실천하는 나만의 방법..? 이런식으로 접근하여 작성했다. 이-공계에서 흔히 정리, 이론, 원리, 정의등 비슷한 뉘앙스의 단어가 많다.면밀히 살피면 그 뜻이 조금씩은 다르다. 우리가 다룬 SOLID는 principle을 약어로 쓴다.원리(原理, principle)는 사물이나 현상의 근본이 되는 이치로 기초가 되는 근거 또는 보편적 진리위와 같은 내용이 principle의 사전적 정의이며, 이렇기에 나는 나만의 정리가 아닌 나만의 실천 방법으로 이 미션을 접근 한 것이다. https://romanc3.tistory.com/130위 내용은 따로 블로그에 작성해뒀다.🤔 내 생각을 정리하자면섹션 5까지 듣고 생각을 정리하자면, 섹션 2와 3은 조금은 더 사고와 밀접하고4와 5는 기법에 가깝다는 생각을 했다. 물론 사고도 포함 하겠지만 말이다. 여기까지 들으면서, 새로운 사고법과 그리고 객체를 만드는 다양한 관점을 알 수 있어서 좋았다.이것은 비단 강의 뿐만 아니라 워밍업 클럽을 통해 다양한 사람들의 해결 방법을 볼 수 있다는 것이 큰 장점 이지 않았나 싶다. 반면에 아쉬웠던 점은 무릇 이러한 부분들이 다 그렇듯 체화 하기가 어렵다는 점이다.마치 가끔씩 쓰는 단축키 처럼 평소에는 까먹고 필요할 때 다시 찾아본다. 분명 그 단축키가 유용함에도 말이다. 그럼에도 아예 존재를 모르는 것과 아! 그런게 있었지? 하고 생각이 나는 것은 다른 부분이라 생각한다.이 강의를 통해 그런 것을 미리 알아가는 것은 분명 좋은 경험이다. 앞으로는 한번 씩 해서는 체화 시키기 힘든 이러한 부분들은 의도적으로 계속 생각하고 따로 간단한 기능들을 만들어가면서 훈련해봐야겠다는 생각을 한다.

백엔드워밍업

이예진

인프런 워밍업 클럽 Backend - 2주차

~ 이번주 요약 ~엔티티(테이블에 맵핑되는 자바 클래스)를 개발하고, @Component, @PostConstruct 어노테이션을 통하여 db초기화.db와 상호작용하는 엔티티를 쉽게 관리하기 위해 각각의 엔티티의 리포지토리 개발 및 리토지토리 테스트 코드작성 후 확인.fetch join을 통한 리포지토리 성능 개선. (N+1 문제 해결 위함)presentation api, view controller를 만들어, api에 따라 어떤 것을 불러오고 실행시킬지 작성 (@RestController, @RequestMapping, @GetMapping, @Controller 등 사용)엔티티를 서비스 내부적으로만 사용하고, 외부로 노출되는 것은 dto로 하기위해, dto개발.도메인의 기능들을 활용하여 프레젠테이션 레이어에 필요한 db 조회, 저장 같은 기능들을 한번 더 랩핑을 해서 사용하는 그런 목적을가진 레포지토리 개발. (프레젠테이션 레이어에 필요한 기능들을 이 레포지토리에 묶어가지고 관리하도록 한다.)각각의 페이지에서 사용할 수 있도록 비즈니스 로직을 처리할 수 있도록 서비스 어노테이션을 사용해 서비스를 개발.그 서비스 코드가 잘 작동하는지 확인하기 위해 테스트 코드 작성 및 확인.   ~ 추가로 더 학습하고 익혀야 할 것들 ~퍼사드 패턴 - 복잡한 시스템을 단순화하여 클라이언트가 쉽게 접근할 수 있도록 인터페이스를 제공하는 디자인 패턴DTO - 데이터 트랜스퍼 오브젝트. 테이터를 담는 통 같은 역할을 하는 자바객체.두개의 레포지토리의 차이 -엔티티와 관련된 데이터 접근 작업을 수행하며, 데이터베이스와 상호작용 할 수 있는 레포지토리 - repository 파일 내의 kt코드들다양한 레포지토리의 필요한 기능들을 한번에 묶어서 관리하도록 함. 복잡한 데이터 액세스 로직 단순화하는 퍼사드 패턴 적용하여 관리 쉽도록 하기위한 레포지토리 - PresentationRepository.ktDataInitialier.kt 기능 - 스프링 애플리케이션이 실행될 때 특정 테스트 데이터를 데이터베이스에 초기화하는 역할. 스프링 프로파일이 "default"로 설정된 경우에만 이 클래스가 동작.주요기능으로는 프로파일 기반실행(@Profile(value = ["default"])), 의존성주입, 초기화 작업(@PostConstruct), 테스트 데이터 저장, 개발/테스트 환경에서의 사용으로 구성.테스트 데이터 저장에서는 성과, 소개, 링크, 경험, 기술, 프로젝트 등의 데이터를 리스트로 생성하고, 각각의 JPA 레포지토리를 이용해 데이터베이스에 저장. 여러 엔티티들을 연관 지어, 더 복잡한 객체 관계도 초기화(Experience와 ExperienceDetail, Project와 ProjectDetail 및 ProjectSkill 등). 각각의 코드의 존재 이유(?), 기능을 알아야함. (전체적인 작동방식, 상호작용)후에 프로젝트를 하기 위해선 각각의 어노테이션에 대한 기능을 익혀야 할 것 같다. (아직도 너무나 헷갈림...)    ~ 미션 ~계획해뒀던 주요 기능에 맞게 api설계회원가입, 로그인, 로그아웃, 캐릭터 등록 - post회원 탈퇴, 캐릭터 삭제 - delete캐릭터 수정 - put캐릭터 검색, 보유 캐릭터 수 확인 - get(자세한건 깃허브 참고 - https://github.com/Dalrae03/my-character-collection)

한나

✏️[인프런 워밍업 클럽 2기] 프로덕트 디자인 2주차 🐾

이번 주는 디자인 시스템의 핵심인 UI 컴포넌트들을 직접 만들어보는 한 주였습니다. 다양한 입력, 디스플레이, 피드백 컴포넌트들을 구현하면서 각 요소의 중요성과 사용자 경험에 미치는 영향을 체감할 수 있었습니다. 특히 작은 디테일 하나하나가 전체 인터페이스의 일관성과 사용성에 큰 영향을 준다는 점을 깨달았습니다.회사 서비스에 테이블 컴포넌트가 사용되는 부분이 있어 직접 구현해보며 기존에 놓쳤던 중요한 포인트들을 재점검하는 유익한시간이 되기도 했습니다. 이 과정을 통해 실무 적용에 대한 더 깊은 이해를 할 수 있게 된 것 같습니다. :D이번 주는 프롬프트를 활용한 디자인 문서화에 대한 특별 강의가 있었습니다.🤖 Chat GPT를 이용한 프롬프트 엔지니어링과 문서화 방법, 그리고 유용한 플러그인들에 대해 알려주셨는데, 실무에서 바로 적용해볼 수 있다는 점에서 큰 기대가 되었습니다. 직접 프롬프트 작성을 연습해보면서 다른 분들의 결과물도 검토해보며 보완점을 찾아볼 수 있었고 AI 도구의 효과적인 활용법을 깊이 이해할 수 있었습니다😊 📌배움입력 컴포넌트: 버튼, 체크박스, 라디오 버튼, 스위치, 텍스트 필드 등디스플레이 컴포넌트: 아바타, 아코디언, 뱃지, 툴팁, 칩스, 테이블 등피드백 컴포넌트: 알림/경고 메시지, 토스트, 스켈레톤 로더, 프로그레스 바 등Chat GPT의 효과적인 프롬프트 작성법: 명확하고 구체적인 지시를 통해 원하는 결과 얻기AI를 활용한 문서화 프로세스: 컴포넌트 설명, 사용 가이드라인 등을 빠르게 작성디자인 관련 유용한 플러그인📌미션이번 주 미션으로 다양한 UI 컴포넌트들을 직접 구현해보았습니다. 아바타, 아코디언, 툴팁, 칩스, 베이직 카드, 테이블 등 여러 디스플레이 컴포넌트를 만들면서 각 요소의 세부적인 디자인과 기능에 대해 깊이 고민할 수 있었습니다. 각 요소의 특성, 상태 관리의 중요성을 깊이 이해해보는 시간이 되었습니다.그리고 특강을 통해 배운 내용을 통해 AI도구를 효과적으로 활용해 생산성을 향상시키고, 작업효율을 높여나가는 프로세스를 스스로 더 연습해야되겠다는 생각이 들었습니다.❗이건 꼭 알아두자! ꙳꒰ ੭⑅•͈ ·̮ •͈꒱੭각 컴포넌트들의 특성상태 관리의 중요성 (기본, 호버, 활성화, 비활성화 등) 컴포넌트 간 일관성 유지를 위한 디자인 토큰 활용챗 GPT 프롬프트 방법과 이를 활용한 문서화 방법📌회고스스로 칭찬하고 싶은 점들었던 강의 내용을 복습하며 디자인 시스템에 대한 이해도를 높이려 노력했습니다.다양한 UI 컴포넌트를 구현하며 세밀하게 검토했습니다. 아쉬웠던 점매일 꾸준히 학습하려 했으나, 야근으로 인해 일부 날짜에는 미션을 당일에 수행하지 못했습니다.문서화 작업에 대한 추가적인 연습이 부족했던 것 같습니다. 보완하고 싶은 점문서화 스킬 향상을 위해 추가 학습 및 연습을 진행하려고 합니다.프롬프트를 활용해 문서화를 더욱 효육적으로 진행해보고 싶습니다. 1주차 미션 때 문서화 진행을 추가적으로 보완해서 진행해보고자 합니다.다음 주 계획주말에 강의를 미리 숙지하고 매일 퇴근 후 2시간 확보 후 미션에 집중하기문서화 작업 시 개발자들과 이야기를 나누고 업무에 다시금 체계적으로 적용해보기한 주 배운 내용을 실무에 적용하며 현 디자인시스템의 보완점을 찾아보기 실제 프로젝트에 미션으로 구현해 본 컴포넌트들을 적용해보기

UX/UIUXUI피그마프로덕트디자인볼드UX

한나

✏️[인프런 워밍업 클럽 2기] 프로덕트 디자인 1주차 🐾

회사 서비스를 구축하는 과정에서 일관된 디자인의 필요성을 절실히 느꼈습니다. 특히 개발자들과의 원활한 소통을 위해 디자인 시스템이 꼭 필요하다는 것을 깨닫던 중에 볼드UX튜터님의 강의를 수강하게 되었습니다.클럽 참여 전에는 시간 제약으로 인해 초반 내용과 필요한 부분만 선택적으로 들었던 터라 아쉬움이 있었습니다. 그러던 중에 인프런 워밍업 클럽 모집 소식을 듣고 참여하게 되었습니다.💪아직 1주차이지만 클럽에 참여하면서 처음부터 강의를 다시 듣고 기초를 다시 다지는 시간이 되었습니다. 그리고 놓친 부분을 다시금 보완하고 현재 회사에서 적용 중인 디자인 시스템도 보완해나가는 과정이 되어가는 것 같습니다. 미션을 통해 체계적으로 학습할 수 있어 스스로 한단계씩 발전되어간다고 느껴져 평소보다 하루일과가 바빠졌지만 과정 하나하나가 소중한 것 같습니다😊📌배움스타일 가이드 작성 : 색상, 타이포그래피, 간격, 아이콘, 그리드, 그림자 적용=> 문서화로 내보내기UI요소 제작 : 버튼, 라디오 버튼, 체크박스 등 기본 컴포넌트 디자인📌미션스타일 가이드 요소별 적용 및 문서화기본 UI컴포넌트 디자인 및 제작 배리어블을 하나씩 적용하고 문서화 작업을 해보면서 실무에서 놓친 포인트들을 짚어보게 되었고 디자인의 일관성이 얼마나 중요한지 실제적으로 느낄 수 있었던 것 같습니다.단순히 색상이나 폰트를 정리하는 작업으로 스스로 생각했던 것 같아요. 하나의 프로젝트의 프로세스 중에 디자인 시스템을 구축한다는 건 정체성을 만드는 과정이라고 생각이 들기도 했습니다.미션과제를 하면서 UI적으로 이쁘니까라는 단순한 생각이 아니라 실제 프로젝트를 구축한다고 생각하고 가독성, 브랜드톤도 고려하며 구축해나가다보니 디테일이 얼마나 중요한지 이해하게 된 시간이었습니다.사용자와 개발자, 비즈니스 목표를 모두 고려해야 하는 종합적인 작업이라는 생각도 들고 그만큼 워밍업 클럽 기간에 실무에도 다시 보완해서 적용하고 디자이너로서 더 예민하고 세밀하게 프로세스를 구축해나가야겠다는 생각도 들었습니다.❗이건 꼭 알아두자! ꙳꒰ ੭⑅•͈ ·̮ •͈꒱੭디자인 시스템의 실무 적용 방법일관된 디자인 구축을 위한 스타일 가이드 작성 프로세스UI 컴포넌트 설계 시 고려해야 할 사항들📌회고스스로 칭찬하고 싶은 점회사 이슈로 업무가 많아지다보니 미션 수행을 바로 진행하지 못해 어려움이 있었지만 조금씩이라도 진도에 맞춰 미션을 달성해나가려고 노력했습니다! :D들었던 강의도 다시 처음부터 학습하며 디자인 시스템의 이해도를 높이려고 노력했습니다. 아쉬웠던 점매일 꾸준히 하기로 OT 때 스스로 약속을 했지만 매일 이행을 하지 못해 아쉬움이 있었습니다.문서화 작업에 대한 추가적인 연습이 많이 필요함을 느꼈고 스스로가 미션달성에 좀 더 치중된 것 같아 아쉬움이 있었습니다. 문서화는 다시 연습을 해보려고 합니다!보완하고 싶은 점매일 정해진 시간에 미션과 강의를 수행하는 루틴 만들기 [OT 때 스스로의 약속을 지켜나가기 위한 계획을 다시금 세우려고 해요!]문서화 스킬 향상을 위한 추가 학습 및 연습다음주 계획주말에 강의를 미리 숙지하고 매일 퇴근 후 2시간 확보 후 미션에 집중하기문서화 작업 시 개발자분들과 이야기를 나누고 업무에 다시금 체계적으로 적용해보기한 주 배운 내용을 실무에 적용하며 현 디자인시스템의 보완점을 찾아보기  

UX/UIUXUI피그마프로덕트디자인볼드UX

예은

[인프런 워밍업 클럽 2기] 프로덕트 디자인 2주차

볼드UX 튜터님의 <피그마 배리어블을 활용한 디자인 시스템 구축하기>를 수강한 지 2주...ㅠㅠ이번 과제물 중 하나는 늦게 제출하고 말았다.커리큘럼에 맞춰 착실히 완주하고 싶었는데... 그래도 학습하는 것이 더욱 중요하다고 생각하고 열심히 마무리했다.아쉬움을 뒤로 하고, 포기하지 않고 나아가겠다고 다짐했다. 배움입력 컴포넌트: 버튼, 체크박스, 라디오, 스퉤치, 라벨, 컨트롤 그룹, 텍스트 필드, 텍스트 상자, 셀렉트와 드롭다운디스플레이 컴포넌트: 아바타, 아코디언, 뱃지, 툴팁, 디바이더, 팁, 카드, 테이블피드백 컴포넌트: 알림/경고 메시지, 토스트, 스켈레톤 로더와 로딩 스피너, 프로그레스 바, 슬롯 컴포넌트, 모달 미션입력 컴포넌트 제작컴포넌트를 조합해 설문조사 페이지, 회원가입 페이지 제작디스플레이 컴포넌트 제작테이블 디자인피드백 컴포넌트 제작모달 디자인(너무너무 힘들었다...!!) 이건 꼭 알아두자포기하지만 않으면 된다!! 회고스스로 칭찬하고 싶은 점기본 미션뿐 아니라 보너스 미션까지 모두 해냈다. 아쉬웠던 점진도를 따라가지 못하고 일정이 밀렸다.보완하고 싶은 점진도를 따라가기에 충분할 만큼의 학습 시간을 확보해야겠다.다음주에는개인 프로젝트에 적용하며 복습할 것이다.

UX/UIUXUI피그마프로덕트디자인볼드UX

SK

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

2주차 학습 요약강의 1 - 따라하며 배우는 자바스크립트 A-Zhttps://www.inflearn.com/course/따라하며-배우는-자바스크립트 - John Ahn강의 2 - 따라하며 배우는 리액트 A-Zhttps://www.inflearn.com/course/따라하는-리액트 - John Ahn6일차 Iterator, Generator, Design PatternSymbol type, Iterator, GeneratorSymbol type이 타입의 목적은 유니크한 식별자를 만들기 위해 사용됨IteratorIterable : 배열은 반복 가능한 객체이며, 반복이 가능하다는 것을 iterable 이라고 부른다. for…in 을 사용할 수 있거나 Symbol.iterator() 값을 가지면 iterable 한 것iterrator : 반복자는 next() 를 호출해서 {value: , done:} 두개의 속성을 가지는 객체를 반환하는 객체Generator사용자의 요구에 따라 다른 시간 간격으로 여러 값을 반환할 수 있다.일반 함수 → 단 한번의 실행으로 함수 끝까지 실행된다.제너레이터 함수 → 사용자의 요구에 따라 일시적으로 정지될 수도 있고, 다시 시작될 수도 있다.자바스크립트 패턴프로그래머가 응용 프로그램이나 시스템을 디자인할 때 일반적인 문제를 해결하는데 사용할 수 있는 공식화된 모범 사례장점최고의 솔루션 : 여러번 수정 하면서 완성되었기 때문에 디자인 패턴은 이미 잘 작동한다는 것을 알고 있다.재사용 성 : 단일 문제에만 존재할 수 없으므로 여러 문제를 해결하기 위해 특정 상황에서 수정할 수 있는 재사용 가능한 솔루션을 나타냄풍부한 표현력 : 큰 문제를 부분적으로 효율적으로 설명할 수 있기 때무에 더 이상의 설명이 필요하지 않음.향상된 의사 소통 : 디자인 패턴에 익숙한 개발자는 문제에 대한 공통 목표를 설정하여 잠재적인 문제와 이러한 문제에 대한 솔루션에 대해 서로 의사 소통하는 데 도움이 된다.필요없는 코드 리팩토링 : 종종 다양한 문제에 대한 최적의 솔루션으로 불림.코드베이스 크기 감소 : 유일한 최적의 솔루션인 디자인 패턴은 공간을 거의 차지하지 않고 몇 줄의 코드로 필요한 솔루션을 구현하여 공간을 보존함.Singleton Pattern클래스의 인스턴스화를 하나의 객체로 제한하는 디자인 패턴. 클래스가 존재하지 않는 경우 클래스의 새 인스턴스를 생성하는 메서드로 클래스를 생성하여 구현할 수 있다. 인스턴스가 이미 존재하는 경우 해당 개체에 대한 참조를 반환한다.Factory Pattern특수 함수인 팩토리 함수를 사용하여 비슷한 객체를 많이 만들 수 있다. ⇒ 비슷한 객체를 반복적으로 생성해야 하는 경우 사용Mediator Pattern객체 그룹에 대한 중앙 권한을 제공한다.Observer Patternevent-drivent 시스템을 이용하는 것을 말함.특정 Subject를 관찰하는 많은 옵저버가 있다. 관찰자는 기본적으로 관심이 있고 해당 주제 내부에 변경 사항이 있을 때 알림을 받기 원한다. 그래서 그들은 그 주제에 스스로를 등록 한다. - youtube 의 구독 및 알림 설정 느낌Module Pattern코드를 더 작고 재사용 가능한 조각으로 분할하는 것을 도와준다. 더 큰 파일을 여러 개의 더 작고 재사용 가능한 조각으로 분할 하는 좋은 방법. 또한 모듈 내의 값은 기본적으로 모듈 내에서 비공개로 유지되고 수정할 수 없기 때문에 코드 캡슐화를 촉진한다. export 키워드를 사용하여 명시적으로 내보낸 값만 다른 파일에서 액세스 할 수 있다.사용하기 위해서 script 선언 할때 type=”module” 을 선언 하면 된다.엄격 모드 실행, 모듈 레벨 스코프, 한번만 실행,6번 과제 - 비밀번호 생성 앱https://pass-create-app.vercel.app/ - 완성 화면https://github.com/ygvbhy/pass-create-app - 코드빌드 도구 Vite CSS Bootstrap 5 (CDN) js Vanilla 배포도구 Vercel해결 과정체크박스를 기준으로 비밀번호를 생성 해야 하는 문제체크 박스가 없으면 비밀번호 생성이 안되므로 number의 체크박스는 강제 선택하게 진행form 으로 감싼 데이터는 기본적인 동작을 수행함. length 에 required 옵션을 넣으면 min 값과 max 값에 의하여 1차적으로 걸러지며 안내 메시지가 출력됨. 범위 사이 라면 event.preventDefault() 를 넣어줘서 기본 동작을 막는다.각 체크 박스 마다 비밀번호를 랜덤하게 생성 해줘야 함. 그래서 해당 체크 박스마다 String 값을 따로 설정 하여 문자열을 만들어 주고 생성하기 버튼을 클릭 했을때 체크된 항목을 찾아서 문자열을 만들어준다. ex) “abcdefghijklmnopqrstuvwxyz0123456789” 이런식이제 이걸 Math.random 함수를 이용해 length 의 길이에서 숫자를 뽑고 해당 숫자에 있는 문자열을 가져온다. → 이걸 입력한 length 만큼 반복출력 된 비밀번호를 복사 버튼을 클릭하면 클립보드로 복사가 된다. navigator.clipboard api 활용 → 복사에 성공하면 alert 창이 뜨며 복사된 항목이 출력됨.7일차 프로젝트 만들기 - Stop Watch, Todo App, SpeadSheet AppStop Watch appsetInterval의 사용법을 배움. setTimeout 과는 또 다른 사용 방식setTimeout : 설정한 시간 (ex - 1000 ⇒ 1초) 가 지나면 내부에 선언한 함수를 실행setInterval : 설정한 시간 (ex - 1000 ⇒ 1초) 마다 내부에 선언한 함수를 실행Todo ApplocalStorage 사용방법과 프론트엔드에서 사용할 수 있는 기능들이 함축적으로 담겨 있는 앱localStorage.setItem(’{사용할 아이디 (ex - todos)}’, {저장할 값})localStorage.getItem(’{설정한 아이디}’)Array 내용은 String 으로 바꿔주고 저장해야 한다.JSON.stringify(arr)이후 getItem 으로 가져온건 String 이 된 Array 이기 때문에 다시 Array 로 바꿔준다.JSON.parse(data)SpewadSheet App난이도가 난이도 인 만큼 제일 길었던 강의간단한 듯 하면서 간단하지 않는 프로젝트Blob(Binary Large Object, 블랍)이미지, 사운드, 비디오와 같은 멀티미디어 데이터를 다룰 때 사용할 수 있다.데이터의 크기(Byte) 및 MIME 타입을 알아내거나, 데이터를 송수신을 위한 작은 Blob 객체로 나누는 등의 작업에 사용한다.Blob 객체는 파일류의 불변하는 미가공 데이터를 나타냅니다. 텍스트와 이진 데이터의 형태로 읽을 수 있으며, ReadableStream으로 변환한 후 스트림 메서드를 사용해 데이터를 처리할 수도 있다.출처https://developer.mozilla.org/ko/docs/Web/API/Blob여기까지가 따라하며 배우는 자바스크립트 A-Z 강의의 마지막이다.강의를 다 듣고 이전 과제들의 코드를 살펴봤을때 정말 못했다는 느낌을 받았다. 이번 리액트까지 정규 과정이 끝나면 리팩토링을 한번 해볼 생각이다.7번 과제 - 타이핑 테스트 앱https://typing-test-henna.vercel.app/ - 완성 화면https://github.com/ygvbhy/typing_test - 코드빌드 도구 Vite CSS Bootstrap 5 (CDN)js Vanilla 배포도구 Vercel해결 과정여태까지의 과제중에 제일 생각을 많이 한 과제아래의 식을 참고함.WPM : (타이핑한 총 문자 수 / 5) / 경과 시간(분 단위) CPM : 타이핑한 총 문자 수 / 경과 시간(분 단위) ACC : (모든 글자수 - 틀린 글자수) / 모든 글자수 * 100 모든 식은 공백 포함 테스트 문자열은 html 공부 할때 많이 나오는 “Lorem ipsum dolor sit amet…” 이 문자열로 진행. html 에서 Lorem 을 치면 vsc 에서 자동완성을 제공. 얼마나 많이쓰면해당 문자열을 split 으로 자른 뒤 각 문자마다 span 으로 감싸고 진행함.textarea 에 input 이벤트를 넣고 입력 될때마다 정답과 비교 하여 정답이면 초록색 글씨, 오답이면 빨간 글씨로 변경8일차 중간 점검9일차 리액트 기본 및 Todo 앱 만들기여기서 부터 리액트 강의가 시작 된다.https://www.inflearn.com/course/따라하는-리액트/dashboard - John Ahn리액트란?리액트는 프레임워크가 아닌 라이브러리이다.프레임워크 vs 라이브러리프레임워크 : 어떠한 앱을 만들기 위해 필요한 대부분의 것을 가지고 있는 것.라이브러리 : 어떠한 특정 기능을 모듈화 해놓은 것리액트가 라이브러리인 이유는 리액트는 전적으로 UI 를 렌더링 하는 데 관여하기 때문.리액트를 도와주는 라이브러리라우팅 : react-router-dom상태 관리 : redux테스트 : jest리액트 컴포넌트컴포넌트 : 리액트로 만들어진 앱을 이루는 최소한의 단위클래스형과 함수형 두가지로 컴포넌트를 나눌 수 있다.리액트의 Hooks 가 나온 뒤론 클래스형 보다 함수형으로 개발이 많아짐npx create-react-app 이 오류가 나서 해결 했다.오류 내용중에 캐시문제가 있어서 캐시를 전부 삭제 한 뒤 다시 실행 했더니 오류 없이 설치가 되었다.sudo npm cache clean -f SPA ( Single Page Application )하나의 HTML 페이지로 구성된 웹 애플리케이션을 의미한다.장점빠른 사용자 경험더 나은 성능모바일 친화적클라이언트 측 라우팅JSX ( JavaScript extension )자바스크립트의 확장 문법. jsx 를 이용해서 ui 작업이 가능하다.React State리액트에서 데이터가 변할 때 화면을 다시 렌더링 해주기 위해서는 React State 를 사용해야 한다.컴포넌트의 렌더링 결과물에 영향을 주는 데이터를 갖고 있는 객체. State가 변경되면 컴포넌트는 리랜더링된다. 또한 State 는 컴포넌트 안에서 관리된다.React Hooksclass 없이 state 를 사용할 수 있는 새로운 기능State 와 Propsstate : 부모 컴포넌트에서 자녀 컴포넌트로 데이터를 보내는게 아닌 해당 컴포넌트 내부에서 데이터를 전달 하는 방식props: 부모 컴포넌트에서 자녀 컴포넌트로 데이터를 전달하는 방법React.memo강의에서 진행중인 Todo App 에서 컴포넌트들은 App, Lists, List, Form 이렇게 4개 존재 하는데 Form 에서 input에서의 input 이벤트를 받았을 경우 input 만 재랜더링 하면 되지만 현재 모든 컴포넌트가 렌더링 되고 있다. 이 문제를 해결 하기 위해선 적용해야 하는 컴포넌트에 React.memo 를 감싸주면 된다.useCallback위의 예시와 마찬가지로 App 컴포넌트에서 생성된 함수를 Lists 를 거쳐 List 까지 Props 로 내려온다면 App 컴포넌트가 재 렌더링 될때 마다 함수가 새롭게 선언 되고 그로인해 Props 로 받아가는 Lists 와 List 까지 재렌더링 된다. 그러므로 useCallback 을 감싸주고 타겟을 정한 뒤 선언 하면 타겟이 바뀌지 않는한 함수를 새로 생성되지 않는다.useMemo 를 이용한 결과 값 최적화메모이제이션(Memoization)은 비용이 많이 드는 함수 호춣의 결과를 저장하고 동일한 입력이 다시 발생할 때 캐시된 결과를 반환하여 프로그램의 속도를 높이는 데 줄로 사용되는 최적화 기술Component 내의 compute 함수가 만약 복잡한 연산을 수행하면 결과 값을 리턴하는데 오랜 시간이 걸리게 된다. 이럴 시에 컴포넌트가 계속 리 렌더린 된다면 계속 수행하는데 오랜 시간이 걸려서 성능 저하가 발생 한다. 이걸 해결 하는게 useMemo . compute 함수에 넘겨주는 값은 이전과 동일 하다면 리 렌더링이 되더라도 연산을 다시 하지 않는다.function Component({a, b}) { const result = useMemo(() => compute(a,b), [a, b]) return <div>{result}</div> } 8번 과제 - 예산 계산기 앱https://cal-app-vert.vercel.app/ - 완성 화면https://github.com/ygvbhy/cal-app - 코드빌드 도구 Vite CSS tailwind css js React 배포도구 Vercel Library izitoast, react-beautiful-dnd해결 과정강의인 Todo 만들기와 매우 흡사한 과제이며 방식이 비슷함.강의 에선 webpack 을 썻지만 과제는 vite를 사용해서 jsx 로 작업 해서 추가적으로 요구 하는 사항이 있다보니 거기서 시간이 오래 걸렸음 ( propTypes 등등 )총 지출 금액 계산에서 아직 배우진 않았지만 useEffect 기능을 사용함후기이번주는 특히 바쁜 주였다. 현업도 진행 중이고 스터디도 진행 하려 하다보니 몸이 힘들었는지 영 집중이 되질않고 잠을 많이 잤다.. ;몸 상태를 핑계로 미루고 미루다가 일요일에 허겁지겁 하는 결과를 낳았다. 다음주에는 빠릿빠릿하게 하고 싶긴 한데 앞으로 휴일이 없다 ㅎㅎ

프론트엔드워밍업클럽프론트엔트2주차발자국

SK 1개월 전

[워밍업 클럽 2기 - Clean Code & Test Code] 2주차 발자국

워밍업 클럽 2기: Clean Code & Test Code의 2주차 발자국 작성입니다.1주차 발자국 보러가기 ✍ 학습 내용 복습이전의 클린 코드에 대해 학습한 내용을 직접 코드에 적용해서 리팩토링하는 시간을 가졌습니다.메서드 추출 시 고려할 점중복 제거무조건적인 중복 제거 보다는 상황에 맞춰서(어설픈 중복 제거 보다는 사람들이 이해하기 쉬운 코드가 더 좋음!)주변의 추상화 레벨과 동떨어져 있는지 항상 확인 객체의 책임 파악 시 고려할 점책임은 상황에 따라서 변할 수 있음예시특정 주문의 할인 금액과 총 가격을 출력하는 기능이 있다고 가정하자. 할인 금액과 총 가격이 단순히 출력 용도로만 사용된다면 해당 가격 계산을 출력을 담당하는 클래스에서 정의할 수 있겠지만, 보통은 추후에 다른 곳에서도 사용될 가능성이 있기 때문에 주문 클래스의 책임으로 만드는 것이 더 좋은 설계일 가능성이 높다(물론 이것은 상황에 따라 언제든지 변할 수 있다) 🤔 회고처음부터 내가 설계한 코드를 리팩토링 하는 것과 남의 코드를 리팩토링 하는 것은 굉장히 다르다는 것을 체감했다남의 코드 리팩토링 하는 것이 더 어렵다 😭그냥 강의만 듣고 넘어가는 것 보다, 코드를 작성하고 강사님의 코드와 비교하는 것이 학습에 훨씬 도움이 된다!🔍 참고Readable Code: 읽기 좋은 코드를 작성하는 사고법

백엔드워밍업클럽2기백엔드클린코드-테스트코드발자국2주차

vin

[인프런 워밍업 클럽 BE 2기] 백엔드 프로젝트 - 2주차 발자국

1주차에는 간단한 엔티티와 프로젝트 구조 작성을 완료하고, 이번 주에는 리포지토리, DTO, 서비스 계층을 전반적으로 개발했으며, 그 과정에서 테스트 코드도 함께 작성했다. 이를 통해 프로젝트의 핵심 기능들을 구조화하고, 안정성을 높이는 데 중점을 두었다. 데이터를 다루는 리포지토리 개발하기리포지토리 개발을 하였으며 역시 1주차와 달리 혼동스러운 어노테이션 및 개념과 처음 접하는 어노테이션 및 개념들이 나왔었다. 하나하나 상세히 어떤 역할을 하는지 강의에서 알려주셔서 따라가는데 문제 없었으나, 중간에 오류가 한번 발생하여 찾아보니 JPA영속성 관련 부분이였다.Projectskill에 데이터가 들어가지 않아 확인을 해보니 project 엔티티에서 skill 부분에 cascade 부분을 빼먹어서 함께 저장이 되지 않았다.1. Spring Component와 Bean 등록Component Bean 등록:스프링에서는 @Component, @Controller, @Service, @Repository 어노테이션을 통해 클래스를 Bean으로 등록@Component: 기본적인 스프링 컴포넌트 등록 어노테이션.@Controller: 웹 컨트롤러 계층에 사용, 요청을 처리하는 클래스.@Service: 서비스 계층에 사용, 비즈니스 로직을 처리하는 클래스.@Repository: 데이터 접근 계층에 사용, 데이터베이스와 상호작용하는 클래스.이들은 모두 @Component를 상속한 어노테이션으로, 역할에 따라 더 구체적으로 분류@ComponentScan:스프링이 애플리케이션을 시작할 때, 이 어노테이션을 사용하여 해당 패키지나 하위 패키지에서 컴포넌트를 스캔하고, DI(의존성 주입)를 위해 Bean으로 등록2. 스프링 프로파일과 @Profile 어노테이션@Profile:스프링에서 다양한 환경 설정을 관리할 때, 특정 프로파일이 활성화된 경우에만 빈(Bean)을 활성화할 수 있도록 하는 어노테이션예를 들어, @Profile("dev")는 "dev" 프로파일이 활성화될 때만 해당 빈이 등록3. 생성자 주입과 Bean 관리생성자 주입 방식으로 의존성을 주입받아 사용하는 것이 권장. 생성자 내부에서 미리 정의된 Bean을 주입받아 사용하고, 이 과정에서 스프링이 의존성 관리를 담당.@PostConstruct:Bean이 스프링 컨테이너에 등록된 후 실행되는 메서드에 붙일 수 있는 어노테이션. Bean의 초기화를 마친 후 로직을 처리할 때 사용.4. 로그 사용 권장System.out.println()은 성능상 좋지 않으며, 멀티 쓰레드 환경에서 비효율적이기 때문에 로그 라이브러리를 사용하여 로그를 남기는 것이 좋다.5. JPA와 영속성 관리 (Cascade)Cascade: JPA에서 엔티티의 상태 변화에 따라 연관된 엔티티도 함께 변경되도록 하는 옵션.CascadeType.PERSIST: 엔티티를 저장할 때 연관된 엔티티도 함께 저장.CascadeType.MERGE: 엔티티를 병합할 때 연관된 엔티티도 함께 병합.CascadeType.REMOVE: 엔티티를 삭제할 때 연관된 엔티티도 함께 삭제.CascadeType.REFRESH: 엔티티를 새로고침할 때 연관된 엔티티도 함께 새로고침.CascadeType.DETACH: 엔티티가 영속성 컨텍스트에서 분리될 때 연관된 엔티티도 분리.CascadeType.ALL: 모든 작업(PERSIST, MERGE, REMOVE, REFRESH, DETACH)에 대해 적용.예를 들어, Project 클래스에서 ProjectSkill 엔티티와의 연관관계에 CascadeType.PERSIST를 설정하지 않으면, Project가 저장될 때 연관된 ProjectSkill이 함께 저장되지 않을 수 있다. 6. JPA 성능 개선 (findById 오버라이딩)JpaRepository의 findById 메서드를 오버라이드하여 성능을 최적화할 수 있습니다. 예를 들어, 특정 조건에 맞는 쿼리를 커스터마이징하여 데이터베이스 성능을 개선할 수 있습니다.이러한 요소들은 스프링 애플리케이션 개발에서 빈 관리, 의존성 주입, JPA를 통한 데이터베이스 영속성 관리를 포함한 다양한 개발 과정에서 중요한 역할을 합니다. 리포지토리 테스트 하고 성능 개선하기개발한 리포지토리가 제대로 작성 되는지 테스트코드를 작성하였으며, 또한 리포지토리의 성능을 개선하기 위해 JPQL을 따로 작성하여 fetch 전략으로 LAZY를 사용하여도 깔끔하게 출력되게 개선하였다.리포지토리 테스트 코드 작성@TestInstance.Lifecycle.PER_CLASS: 메서드 간 독립적인 실행이 가능하며, 메서드 간 의존성이 제거됨.의존성 주입: 테스트 시 필요한 리포지토리나 서비스 등을 주입받아 테스트를 진행. 테스트 데이터 초기화: @BeforeAll을 사용하여 테스트에 필요한 데이터를 사전 생성. 프록시 객체와 Fetch 전략프록시 객체: JPA에서 연관된 엔티티를 가짜 객체로 생성하여 필요할 때만 데이터베이스에서 불러옴.LAZY 로딩: 필요할 때마다 쿼리를 날려 데이터를 가져옴. 하지만, 반복문을 돌 때마다 SELECT 문이 나가 성능 저하가 발생할 수 있음.EAGER 로딩: 한 번에 모든 연관된 엔티티를 조회하지만, 불필요한 데이터를 미리 가져올 경우 성능에 영향을 미칠 수 있음.3. Fetch Join과 N+1 문제 해결Fetch Join: JPQL에서 사용하여 연관된 엔티티를 한 번의 쿼리로 모두 조회하는 방법.장점: 추가적인 쿼리가 발생하지 않아 성능 최적화가 가능.단점: 필요 없는 데이터를 미리 가져올 경우 메모리 낭비 가능.N+1 문제: Lazy 로딩 시 발생하는 성능 문제로, 하나의 엔티티를 조회할 때 연관된 엔티티를 각각 추가 쿼리로 조회하여 비효율이 발생. Fetch Join으로 이 문제를 해결 가능.  4.Assertions의 assertThatassertThat: 테스트 코드에서 검증을 위한 메서드로, 다양한 조건에 맞는 검증을 수행할 수 있음.isEqualTo(): 두 값이 같은지 비교.isTrue() / isFalse(): 조건이 참인지, 거짓인지 확인.hasSize(): 리스트나 배열의 크기를 검증.contains(): 리스트가 특정 값을 포함하는지 확인.데이터를 조회하고 변환하는 서비스 개발하기도메인 패키지와 연결된 presentation 패키지를 생성하였고, 해당 패키지 안에 클래스, DTO, 리포지토리, 서비스 개발을 완료하였다. 또한, 서비스에 대한 테스트 코드도 작성했다.이번에는 도메인에서 개발했던 리포지토리 테스트와 달리 단위 테스트가 아닌 방법으로 개발하였다. 기존까지 나는 개발 환경에서 페이지를 띄운 후 하나하나 수동으로 테스트를 진행했지만, 이러한 방식은 운영 서버에서 테스트하기 어렵다는 단점이 있었다.따라서 테스트 코드를 작성하여 자동화된 방식으로 테스트하는 것이 실무적으로 더 안정적이라고 한다.1. @RestController로 REST API 구현@RestController: 스프링에서 RESTful 웹 서비스를 만들 때 사용하는 어노테이션으로, 컨트롤러에서 반환하는 데이터를 자동으로 JSON 형식으로 변환해 클라이언트에 전달한다. 주로 API 응답에서 사용되며, HTML이 아닌 데이터를 반환한다.2. @GetMapping과 @RequestMapping@GetMapping: HTTP GET 요청을 처리하는 어노테이션으로, @RequestMapping(method = RequestMethod.GET)의 간편한 대체 방식이다. 이를 통해 서버는 클라이언트의 GET 요청에 대해 데이터를 조회하고 응답할 수 있다.3. DTO (Data Transfer Object)DTO: 데이터 전송 객체로, 서버와 클라이언트 간 데이터를 주고받기 위한 객체다. 비즈니스 로직을 포함하지 않고 데이터의 전송에만 집중되어 있으며, 데이터를 안전하고 일관되게 전달하는 데 사용된다.4. Mockito와 @InjectMocks로 의존성 주입Mockito: 단위 테스트에서 실제 객체 대신 Mock 객체를 사용하여 독립적인 테스트를 가능하게 해주는 테스트 라이브러리이다. 이를 통해 외부 의존성을 최소화한 테스트를 할 수 있다.@ExtendWith(MockitoExtension::class): JUnit5와 Mockito를 통합해, 테스트 환경에서 Mock 객체의 자동 주입을 제공하는 어노테이션이다. 테스트 클래스에서 의존성 주입을 자동으로 처리하며, 의존성을 가진 객체들을 쉽게 Mocking할 수 있다.@InjectMocks: Mock 객체를 주입받아 의존성을 가진 객체를 독립적으로 테스트할 수 있도록 하는 어노테이션이다. Mock 객체를 통해 실제 객체 없이도 테스트할 수 있어, 테스트의 독립성과 효율성을 높여준다.미션 3REST API 설계하기이번 미션에서는 설계한 테이블을 기반으로 REST API를 설계하는 작업을 진행했다. 상품 API, 분류 API, 브랜드 API, 재고 API를 설계했으며, 각 API의 기능을 세부적으로 구현하였다.분류와 브랜드 API에서는 삭제 대신 useYn 필드를 변경하여 비활성화하는 API를 설계했다. 즉, 데이터를 완전히 삭제하지 않고 비활성화 상태로 변경하는 방식이다.재고 API에서는 재고 삭제 API를 설계하지 않았고, 재고를 수정할 때 기존 데이터를 수정하는 대신 새로운 데이터를 생성하여 수정 이력을 로그처럼 남길 수 있도록 설계했다.또한, HTTP 메서드 중 PUT과 PATCH 메서드의 차이점에 대해 고민했는데, 두 메서드의 차이는 다음과 같다:PUT: 전체 리소스를 대체하거나 새로 생성하는 데 사용된다.PATCH: 리소스의 일부만 수정하는 데 사용되며, 부분적인 변경에 적합하다.https://github.com/Malvin222/mission-backoffice 

웹 개발백엔드웹개발워밍업클럽스프링나만의포트폴리오사이트만들기

vin 1개월 전
나는뉴비

워밍업 클럽 스터디 2기 2주차 발자국

강의Readable Code: 읽기 좋은 코드를 작성하는 사고법강의 수강섹션 6. 코드 다듬기주석을 사용할 때 주의사항, 변수 및 메서드 순서에 관련한 고려 사항, 패키지를 나눌 때 고려해야 할 사항 학습섹션 7. 리팩토링 연습미리 나만의 기준으로 리팩토링을 한 이후 해당 섹션 수강내 리팩토링과 강의에서의 리팩토링을 비교하며 실수하거나 놓친 부분을 검토섹션 8. 기억하면 좋은 조언들능동적 읽기 : 도메인을 이해하기 위해 단순히 코드를 읽기만 하는 것이 아닌, 주석이나 리팩토링을 진행하는 것오버 엔지니어링 : 오버 엔지니어링을 경계하고, 왜 이렇게 리팩토링을 진행했는지 항상 근거가 있어야 함은탄환은 없다 : 클린 코드는 중요한 개념이지만 상황에 따라 비교적 중요하지 않아질 수도 있고, 후순위로 미뤄질 수 있음을 명심미션Day 7강의를 듣기 전 studycafe라는 도메인을 구현한 애플리케이션을 나만의 기준으로 리팩토링 진행중간점검 때 리뷰를 통해 놓치거나 잘못 생각한 부분을 바로잡을 수 있었음사용자가 선택한 이용권에 대한 비용 계산을 수행하는 책임을 가진 객체가 어색함과도한 리팩토링 적용으로 인한 코드 중복, 이로 인한 코드의 복잡함파일에서 데이터 조회 시 캐싱회고단순히 강의를 수강하는 것 보다 미션을 통해 미리 리팩토링을 진행하고, 중간 점검 및 강의 수강을 통해 놓친 점, 잘못 생각한 점을 바로잡을 수 있어 많은 도움이 되었다다른 분들이 진행한 리팩토링 코드도 많이 봤는데 다양한 관점이 있음을 알았고, 생각하지 못한 방향성을 파악할 수 있었다리팩토링을 포함한 코드를 작성하는 과정에는 항상 근거가 있어야 한다는 것을 체감했다

웹 개발워밍업

[워밍업 클럽 스터디 2기::백엔드] 2주차 발자국

2주차 클럽 스터디 회고이번주 학습 내용객체지향 적용 하는 방법에 대해서 학습했다.실무에서는 보통 상속 보다 타 객체를 조합하고, 여러개의 구체를 바꾸어가며 사용할 경우 인터페이스를 활용해 설계를 해야 함을 알았다.Value Object (VO)도메인의 어떤 개념을 추상화 하여 표현한 객체로, 객체를 값으로 취급하기 위해, 불변성, 동등성, 유효성 검증을 보장하도록 설계해야 한다.불변성필드의 값이 변하지 않아야 한다. 생성되는 값들은 final (변하지 않는 값) 으로 선언하고, Setter를 사용하면 안된다.동등성객체의 주소가 서로 다르더라도, 내부의 값이 동일하면 같은 객체로 취급해야 한다. (HashCode, equals 재정의를 통해)유효성 검증생성자를 통해 객체를 생성할 때, 객체가 가진 특성에 따라 유효성 검증을 통해 객체가 항상 자신이 가진 역할을 수행할 수 있도록 보장해야한다.Entity 와의 차이점Entity 는 VO에서, 모든 값이 동일할 때만 같은 객체로 인식하는 것이아니라, 식별자가 존재해 해당 식별자가 같을 경우 동일한 Entity 로 인지하며, 시간에 따라 변화한 Entity로 이해할 수 있다.일급 컬렉션단 하나의 Collection 을 가지고 있는 객체이며, 해당 컬렉션을 추상화 시켜 가공로직을 담고 있어, 테스트를 용이하게 만들어 주는 것.하지만, 일급 컬렉션은 가공로직을 가지고 있기 때문에, 일급컬렉션을 통해 특정 조건을 만족하는 반환할 때, 새로운 콜렉션으로 반환 시켜주어야 한다. (외부에서 해당 컬렉션을 조작하지 못하도록)도메인 개념은 만드는 것이 아니라 개념을 찾아가는 것이다.내가 만들어갈 도메인들은 만들어질 기능의 성격에 따라서 관점에 따라서 여러가지 도메인이 만들어질 수 있으며, 만들어질 부분의 성격을 이해해 개념을 찾아가는 과정이다.좋았던점 (Liked)이전주에는 기한에 맞추어서 하지 못했던 부분까지, 2주만에 전체 강의를 완강에 성공했다!이번주차 미션으로 주어진 리팩토링은 고민을 하고, 일이 있어 제 시간에 마무리하지는 못했지만,내가 지금까지 배운 내용을 강의를 보지 않고 직접 고민해보고 나만의 리팩토링을 하고, 라이브 중간점검을 통해 코드리뷰를 하는 부분을 보며, 개선해갈 방향을 알아가게 되었다!아쉬웠던 점 (Lacked)코드리뷰를 제시간에 완료하지 못해 신청하지 못했다..나도 꼭 리뷰를 받아보려고 내가 저번주 주말에 계획한대로 시간을 할애하지 못해 결국 개인 리팩토링 완료를 신청기간내에 하지 못했고, 하루정도 늦춰져서 마무리했다. (마무리 했더라도 내가 작성한 부분에 대해 반성을 많이 했을 것 같다.)물론, 내가 코드리뷰 받지 못했어도, 우빈님이 라이브 때 내가 궁금했던 점은 거의 풀렸으며..3주차에서는, 다른 코드리뷰를 받으신 분들 처럼, readme.md 파일이나, 물어볼 부분의 소주제를 알고, 정리하도록 하는 좀더 기록을 하고, 공유할 수 있도록 할 것이다!배운 점 (Learned)Optional 이라는 Null을 반환할 때 명시적으로 표현해 주는 방법이 왜 필요한지 알게 되었다.앞으로 과제를 통해서나, 기록한 내용을 꼼꼼히 하나하나 분류를 나눠서 진행을 해야함을 알게되었다..앞으로 바라는 점(Longed for)실무에서 지금까지 배운 읽기 좋은 코드 작성하는 방법을 매일매일 상기하며 적용시켜 볼 것이다.아무리 내가 이 강의에 대해 기록을 했다고 하더라도, 계속 고민하고 살펴보지 않는다면, 결국에 다시배우기 이전으로 하나하나 돌아가는 것 같다.. 이제는 작은 한파일 작업할때 정렬하는 부분 부터 시작해서코드를 작업하고 배운부분을 하나하나 적용할 부분은 없는지 이걸 몸에 습관으로 배게 할 것이다!

백엔드

임원기

인프런 워밍업 스터디 클럽 2기 - 백엔드(클린코드, 테스트코드) 2주차 발자국

워밍업 클럽을 엊그저께 시작한 것 같은데 벌써 절반에 다다랐다.2주차의 다룬 내용은 주로 코드 리팩토링과 유지보수에 관련된 주제들이었다.코드 다듬기 파트먼저 주석의 존재 의의부터 시작하여 변수와 메서드 나열 순서로 이어졌다.주석 같은 경우, 필요한 경우에만 쓰는 것이 좋다. 모든 방법을 동원해 코드에 의도를 녹여냈지만 그럼에도 전달해야 할 정보가 남았을때 사용하는 것이 주석이다. 클린 코드에서도 주석에 대해서 '나쁜 코드를 보완하지 못한다'라고 설명하는데 이보다 적합한 표현은 없다고 생각한다.이어서 '논리, 사고의 흐름'의 연장선이라고 볼 수 있는 변수와 메서드의 나열 순서도 다룬다.변수는 사용하는 순서대로 나열하며, 항상 객체 입장에서 생각하자는 것이 핵심이다. 상태가 변경되는 변수 및 메서드를 우선으로 배치하고 판별, 조회 로직이 있는 메서드들 순으로 하는 기준을 배우고 코드에 원칙을 적용하는 리팩토링 하는 과정을 거쳤다. 컨벤션에 대한 정답은 없는 법이지만 협업과 가독성 관점에서 전적으로 동의하는 내용이 주를 이루었다.리팩토링 연습 파트객체 지향 원칙을 적용하고 여태 배웠던 것을 전체적으로 리팩토링 하는 파트이다.먼저 추상화 레벨에 관한 부분이다. 이번 파트는 이미 예제로 작성되어있는 코드를 리팩토링 하는 과정을 거치는 것이어서 살짝 어려웠다.StudyCafePassMachine 클래스에서 코드 중복부분을 제거하고 일급 컬렉션을 적용할 수 있는 부분을 찾아내 변경한 것 까지는 좋았지만 객체에 메시지를 던진다는 것이 익숙하지 않아 많이 해맸다. 강의에서는 Order 객체를 만들어 책임을 분리하는 접근 방법같이 내가 생각하지 못했던 여러 관점을 엿볼 수 있었는데, 확실히 코드는 돌려보면서 평가받아야 문제점과 개선점을 찾을 수 있다는 점을 다시 한번 크게 느끼게 되었다.강의를 쫓아가느라 바빴지만 개인적으로 가장 유익한 파트였으며 미션으로 여태 학습한 것을 동원하여 코드에 대한 고민을 할 수 있는 시간이었다.다른 시험이 겹쳐서 코드 리팩토링에 시간을 많이 투자하지 못해서 아쉬웠지만, 시간을 내서 내 방식으로 다시 리팩토링하여 개선해 볼 예정이다.맺음강의에서 다루는 내용이 상당히 많았지만 결국 '추상'이라는 키워드에 입각하여 이에 벗어나지 않은채 클린 코드의 정수를 학습할 수 있는 시간이었다.무작정 답을 알려준다기 보다는 답을 찾아가는 방법을 유도해주는 우빈님의 방식덕에 워밍업 클럽에 몰입할 수 있었다.3주차부터는 테스트 코드에 관한 내용이다. 이미 한 번 완강했지만 스터디 클럽의 커리큘럼을 통해 다시 돌아보는 좋은 시간이 될 것 같다.이번엔 시험이 있어서 시간 분배에 실패했지만 남은 2주 잘 마무리하도록 시간을 적절히 투자하여 우수러너 욕심을 한 번 내볼 생각이다.러너들 화이팅~출처 우빈님의 강의 

[워밍업 클럽 스터디 2기] 2주차 발자국

강의 출처 Readable Code: 읽기 좋은 코드를 작성하는 사고법 학습 내용코드 다듬기주석 적절히 사용하기자주 변하는 정보는 지양관련 정책이나 코드가 변경될 경우 주석도 업데이트변수와 메서드의 나열 순서변수는 사용하는 순서대로 나열메서드의 경우 객체의 입장에서 고려공개 메서드를 상단에 배치공개 메서드끼리도 기준을 가지고 배치(상태변경 > 판별 >= 조회 메서드)비공개 메서드는 공개 메서드에서 언급된 순서대로 배치패키지 나누기문맥으로써 정보를 제공할 수 있음패키지를 쪼개지 않으면 관리가 어려움(너무 잘게 쪼개는 것도 관리가 어렵다)대규모 패키지 변경은 팀원과 합의 후기능 유지보수IDE의 도움받기코드품질 : sonarlint 포맷규칙 : .editorconfig 리팩토링메서드 추출로 추상화 레벨 맞추기중복 제거, 메서드 추출Optionalsetter 사용 x, 무분별한 getter사용 대신 객체에 메세지 보내기객체의 책임과 응집도IO 통합일급컬렉션 Order 객체 추출추상화 관점의 차이구현에 초점을 맞춘 추상화 / 도메인 개념에 초점을 맞춘 추상화미션사용자가 이용권을 선택하고 선택한 이용권에 따라서 가격을 계산하는 프로그램을 리팩토링하는 미션이었다. 추상화 레벨에 맞지 않는 부분이 있다면 메서드 추출 등으로 추상화 레벨을 맞춰주고, 객체지향 패러다임에 맞게 객체들이 상호 협력하고 있는지, SRP, DIP, 일급 컬렉션 등의 포인트로 리팩토링을 해주면 되었다.회고이번주는 개인적으로 회사의 프로젝트 일정과 겹쳐서 진도를 따라가는것에도 많은 어려움이 있었고, 특히 대부분의 이해 안되는 부분에서 다시 듣지 않고 강의 진행률만 신경써서 넘기는 식으로 진행되어서 많이 아쉬웠다. 다음주부터는 새로운 강의 주제로 넘어가게 되니 밸런스를 잘 맞춰서 강의를 따라잡는데 문제가 없도록 잘 신경써야 할 거 같다.

ppusda

인프런 워밍업 클럽 2기 - 백엔드 프로젝트(Kotlin, Spring) / 2주차 발자국

⭐ 1주 동안 배운 내용을 정리하고 회고하는 시간을 가져보자. 6 ~ 7일차 - Repository, TestFetchType과 N+1 문제강의에서 FetchType에 따른 쿼리 작동 과정을 보여주셨고 이 때 발생할 수 있는 N+1 문제에 대해서 언급이 되었다.N+1 문제는 JPA를 사용하여 Entity를 조회할 때 발생할 수 있는 문제로 아래와 같이 부모 엔티티를 조회할 때, 연관 되어있는 자식 엔티티들의 수 N 만큼의 쿼리가 발생하여 성능에 지장을 줄 수 있는 문제다. 지연로딩을 사용했을 때는 각 엔티티를 실제 사용할 때 마다 쿼리가 발생하였고, 즉시로딩으로 변경하자 모든 엔티티의 정보를 한꺼번에 수집하여 두 경우 모두 N+1 문제가 발생함을 볼 수 있었다.이를 해결하기 위해 JPQL에서 Fetch Join 쿼리를 작성하고 application.yml 을 수정하게 되었다. Fetch Join과 Fetch SizeJPA에서 쿼리를 직접 작성하기 위해서는 JPQL을 사용할 수 있다.아래는 강의에서 작성했던 쿼리 부분이다.@Query("select e from Experience e left join fetch e.details where e.isActive = :isActive") fun findAllByIsActive(isActive: Boolean): List<Experience> Fetch Join을 사용하여 연관관계를 한번에 조회할 수 있었고, 단 한번의 쿼리로 줄어든 것을 볼 수 있다.하지만 이 경우에도 @~ToMany의 관계를 갖는 자식 엔티티가 여러 개인 경우에는 적용할 수 없다는 한계가 있다.이는 MultipleBagFetchException 이 발생하기 때문인데, 그 이유는 다수의 자식 엔티티를 Fetch Join하게 될 경우에 중복이 발생하고 일관성이 떨어지게 된다.그렇기에 JPA에서 이를 방지하기 위해 MultipleBagFetchException 를 통해 두 개 이상의 자식 엔티티를 Fetch Join 하는 것을 막아두었다. 이를 해결하기 위해 Fetch Size를 조정하여 해결할 수 있다.spring: jpa: properties: hibernate: default_batch_fetch_size: 10 기존의 문제점은 자식 엔티티가 여러 개일 경우 하나의 Fetch Join만 사용가능하며, 그로 인해 N개의 쿼리가 더 발생한다는 점이었다.default_batch_fetch_size 를 조정하여 되면 부모 엔티티의 Key를 이용하여 in 절을 통해 조정한 default_batch_fetch_size 만큼씩 자식 엔티티를 조회할 수 있다.Test6~7일차에는 이러한 내용들을 테스트 해볼 수 있는 Repository 테스트 코드를 작성하게 되었다.아래 내용은 강의에서 작성한 코드의 일부분이다.@DataJpaTest @TestInstance(TestInstance.Lifecycle.PER_CLASS) // 클래스 간 독립적으로 실행 됨. class ExperienceRepositoryTest( @Autowired val experienceRepository: ExperienceRepository ) { @DataJpaTestJPA 관련 테스트를 위한 설정을 제공하는 어노테이션이다.그렇기에 데이터에 접근할 수 있는 레이어인 리포지토리 테스트 시 많이 사용된다.내장 데이터베이스를 설정하고, @Entity 및 @Repository 어노테이션이 부여된 클래스들을 통해 테스트 환경을 구성하는 역할을 한다.@TestInstance테스트 인스턴스의 라이프사이클을 지정하기 위해 사용된다.기본적으로 JUnit 5는 각 @Test 메서드마다 새로운 테스트 인스턴스를 생성하게 되어있다.이는 테스트 환경을 어떻게 구성할 것이느냐에 따라 달라지겠고, 상황에 맞춰 사용하면 될 것 같다. 8 ~ 10일차 - DTO, Service, TestDTOKotlin에서는 Java의 Record처럼 data class를 통해 DTO를 선언해 줄 수 있었다.추가적인 생성자를 선언하기 위해서는 constructor 를 이용하여 만들어 줄 수 있었다.map, filter와 같은 컬렉션 함수를 적용할 때 람다식에서 이름을 지정해주지 않아도 it으로 사용할 수 있다.data class ProjectDTO( // 생략 val details: List<ProjectDetailDTO>, val skills: List<SkillDTO>? ) { constructor(project: Project) : this( // 생략 details = project.details.filter { it.isActive }.map { ProjectDetailDTO(it) }, skills = project.skills.map { it.skill }.filter { it.isActive }.map { SkillDTO(it) } ) } Service, Repository도메인에서 관리해야할 Repository가 많아짐에 따라 한 번에 의존관계를 주입받을 수 있는 Repository를 생성했다.각각 필요한 부분만 주입받게 되면 후에 관리하기가 힘들어 지는 상황을 예방할 수 있다.추가적으로 각 리포지토리의 기능들을 래핑하여 캡슐화 하는 형태의 코드를 작성하여 Service 단에서 사용하기 유용하도록 코드를 작성하였다.@Repository class PresentationRepository( // Presentation 에서 필요한 리포지토리들을 한 번에 주입받아서 활용하기 위함. // A, B, C 형태로 따로따로 주입받으면 후에 관리하기가 힘들기 때문 private val achievementRepository: AchievementRepository, // 생략 ) { fun getActiveAchievements(): List<Achievement> { return achievementRepository.findAllByIsActive(true) } // 생략 } Test - Mockito8~10일차에는 Service에 구현한 기능들에 대해서 테스트 코드를 작성하게 되었다.아래 내용은 강의에서 작성한 코드의 일부분이다.@ExtendWith(MockitoExtension::class) // Mockito Extension 추가 class PresentationServiceTest { @InjectMocks // Mock을 주입받을 대상, 테스트를 할 대상 lateinit var presentationService: PresentationService // Mock을 만든 이후 초기화를 진행하기 위해 lateinit @Mock lateinit var presentationRepository: PresentationRepository } @ExtendWith테스트 확장을 지원하는 어노테이션으로 Mock 객체의 생성 및 초기화를 자동으로 처리하게 해주는 역할을 해준다.@InjectMocksMockito에서 테스트 대상이 되는 클래스에 인스턴스를 생성하고, @Mock 이 사용된 필드를 찾아 객체를 자동으로 주입하기 위해 사용된다.위에서는 테스트할 대상인 PresentationService의 인스턴스를 생성하며, PresentationRepository 에 Mock 객체를 주입하기 위한 용도로 사용된다.@MockMockito에서 Mock 객체를 생성할 때 사용한다.Mock 객체는 모의 객체로 실제 객체의 동작을 흉내낼 수 있다.아래는 강의에서 사용된 Mock 객체가 실제 객체의 동작을 흉내낸 부분이다.Mockito.`when`(presentationRepository.getActiveIntroductions()) .thenReturn(activeIntroductions) when에서 정의한 내용을 시도했을 때, activeIntroductions 의 내용을 반환 하도록 Mocking을 한 것이다.presentationRepository가 실제 데이터베이스와 상호작용이 발생하지 않도록 동작을 했다고 속이는 것이며, 이러한 동작을 통해 테스트를 독립적이고 일관되게 유지할 수 있다. 서브 미션2주차의 서브 미션에는 API 설계가 예정되어 있었다.이를 위해 RESTful 하도록 API를 설계하도록 노력해봤고 결과물은 아래 리포지토리에서 볼 수 있다.https://github.com/ppusda/MML 총 회고이번 2주차는 쉬는 날 겹쳐있어 진도가 많이 나가질 못했다.하지만 의외로 많은 걸 배울 수 있었다.단순히 Kotlin으로 Spring을 접근하는 방법 뿐만 아니라 약간의 복습과 거들어 N+1 문제, Test code와 같이 아직 부족한 부분에 대해서 좀 더 학습할 수 있었다.특히 Test code에 대한 부분은 조금 공부해보니 흥미가 더 생겨서 향후에 Mockito 동작 과정에 대해서 자세하게 뜯어볼 의향도 생겼다. 앞으로도 부족한 부분을 채워나가면서 학습해나가야겠다.모두 화이팅! (o゚v゚)ノ 참고https://jojoldu.tistory.com/457

백엔드워밍업클럽백엔드2기Kotlin

재인

[2주차] 인프런 워밍업 클럽 Backend

2주차 학습 내용 스프링 Profile서버 마다 다른 상수 값을 쓰는 방법이다. annotation 종류@Entity테이블과 매핑되는 엔티티임을 명시 @MappedSupperclass객체의 공통 매핑정보 필요 시 해당 어노테이션을 사용한 추상클래스를 만들고 상속받아서 사용한다 @Idpk임을 명시GeneratedValue : pk 생성 전략을 선택한다 @Component스프링이 처음 실행되며 컴포넌트 스캔을 진행하는데 해당 어노테이션이 사용된 것들을 찾아서 인스턴스를 만들어 준다 @PostConstruct스프링 실행되어 모든 빈들이 등록된 이후 수행되는 메서드를 위한 어노테이션 @CreatedDate현재 시간을 필드에 넣어주는 어노테이션@Column(nullable = false, updatable = false) 로 설정해준다 @UpdatedDate수정 시간을 필드에 넣어주는 어노테이션updatable 속성은 디폴트가 true이다 @OneToManyFetchType.LAZY 를 사용하는게 좋다→ Eager는 N+1문제가 생김, 성능 이슈CasecadeType.ALL영속성을 지키기 위해 싱크를 맞춰줌  미션API 문서를 Postman을 이용해 작성해 본게 처음인데, 같이 미션 하는 분들의 자료를 참고하며 진행하니 많은 도움이 되었다. 진행 중에 부딪힌 두가지 이슈가 있었다.먼저 양방향 연관관계에 대해 JPA를 사용해 개발해본 경험이 없어서 어려웠다.매핑테이블을 따로 두었기 때문에 N:M -> 1:N, M:1로 쪼개고1:N 설정 시 FK는 N쪽에 두고, FK 설정할 필드 값을 위해 ManyToOne을 사용해 1쪽에 구현했다.다만 h2에서 테이블 조회시 FK 설정이 보이지 않아, 이 부분은 추가로 확인할 예정이다.추가로 유저 테이블명을 user로 해두었는데, h2 db 2.x.x 버전에서 user 키워드가 예약어로 지정되어있어 오류가 났다.해당 부분이 예약어가 아님을 명시하는 속성을 application.yml에 추가하였다. 회고저번주 회고에 미션이나 발자국을 미리 작성해보고자 했는데, 그렇게 하지 못해서 아쉽다.끝까지 포기하지 않도록 틈틈히 스터디를 진행하고자 한다. referencehttps://mag1c.tistory.com/434https://hudi.blog/spring-data-jpa-auditing-create-update-date/https://velog.io/@jinee/TIL-Postman으로-API문서-만들기-l4k5mj31rlhttps://siyoon210.tistory.com/27https://mag1c.tistory.com/434

백엔드백엔드

유진

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

강의 출처 Readable Code: 읽기 좋은 코드를 작성하는 사고법 학습 내용코드 다듬기주석 적절히 사용하기자주 변하는 정보는 지양관련 정책이나 코드가 변경될 경우 주석도 업데이트변수와 메서드의 나열 순서변수는 사용하는 순서대로 나열메서드의 경우 객체의 입장에서 고려공개 메서드를 상단에 배치공개 메서드끼리도 기준을 가지고 배치(상태변경 > 판별 >= 조회 메서드)비공개 메서드는 공개 메서드에서 언급된 순서대로 배치패키지 나누기문맥으로써 정보를 제공할 수 있음패키지를 쪼개지 않으면 관리가 어려움(너무 잘게 쪼개는 것도 관리가 어렵다)대규모 패키지 변경은 팀원과 합의 후기능 유지보수IDE의 도움받기코드품질 : sonarlint 포맷규칙 : .editorconfig 리팩토링메서드 추출로 추상화 레벨 맞추기중복 제거, 메서드 추출Optionalsetter 사용 x, 무분별한 getter사용 대신 객체에 메세지 보내기객체의 책임과 응집도IO 통합일급컬렉션 Order 객체 추출추상화 관점의 차이구현에 초점을 맞춘 추상화 / 도메인 개념에 초점을 맞춘 추상화미션사용자가 이용권을 선택하고 선택한 이용권에 따라서 가격을 계산하는 프로그램을 리팩토링하는 미션이었다. 추상화 레벨에 맞지 않는 부분이 있다면 메서드 추출 등으로 추상화 레벨을 맞춰주고, 객체지향 패러다임에 맞게 객체들이 상호 협력하고 있는지, SRP, DIP, 일급 컬렉션 등의 포인트로 리팩토링을 해주면 되었다.회고미션을 진행하는 동안 가장 큰 어려움은 전체 코드의 맥락을 파악하는 것이었다. 그러다보니 변수, 메서드명 변경, 메서드 추출 등의 소심한 리팩토링 위주로 미션을 진행했다. 수강했던 강의들의 복습의 필요성을 절실히 느낄 수 있었다. 지난 강의들을 바탕으로 읽기 좋은 코드란 무엇인지 끈임없이 고민하는 자세를 가져야겠다.

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

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

2주차 강의 정리너무 상세한 주석은 오히려 도움이 안될 수 있다의사결정 히스토리를 기록하는 주석을 작성변수와 메소드의 나열 순서패키지 변경은 팀원과 커뮤니케이션 필요Linting - Sonarlint(Spring), cmd + option + Lgit reset --hard를 적극 사용하자 -> 코드를 수동적으로 보기만 하는 것에서 벗어나야함오버 엔지니어링 -> 리팩토링은 기능은 그대로, 구조 변경을 고려은탄환은 없다 -> 적정 기술2주차 강의 회고좋았던 점IDE 사용을 더 잘 알게 되었다.새로운 과제 프로젝트에 리팩토링을 실제로 해볼 수 있었다.아쉬웠던 점생각을 코드로 옮기는 부분이 부족했다.과제 제출을 못했다.배운 점헷갈렸던 git 커밋 단위를 알게 되었다.패키지 단위와 메소드의 나열 순서에 대해 알게 되었다.앞으로 바라는 점헥사고날 아키텍처에 대해 공부해보는 것.미션 Day7고민했던 점{ HOURLY, WEEKLY, FIXED } Enum을 인터페이스 하위에 구현체로 변경해야한다고 생각하였다.고정석만 Locker를 사용할 수 있다는 개념을 어떻게 도메인 개념으로 표현할 것인지 쉽지않았다.사물함을 사용할 수 있는 PassType인지를 확인하도록 표현하는 것이 좋았다.이때 확인 로직은 PassType Enum 안에서 수행좌석과 사물함의 관계를 어떻게 볼 것인가?좌석도 상품, 사물함도 상품이므로 분리되는 도메인으로 생각하였다. -> 중복되는 부분이 많아서 하나의 인터페이스로 추상화하는 것이 좋았다.InputHandler에서 CafePass를 생성하는 것이 맞는 것인지 고민되었다.DTO를 써서 Service에서 initialize를 해야할까 고민하였다.File에서 불러오는 데이터를 처음 실행 시에 메모리에 적재를 하고 비즈니스 로직을 실행해야할까 고민하였다.비즈니스 로직에서 while문을 통해 반복하는 것이 아닌 일회성 로직이라 변경하는 것이 아닌 것 같아보였다.

zooxop

인프런 워밍업 스터디 클럽 2기 백엔드(클린코드&테스트코드) 발자국 - 2주차

인프런 워밍업 클럽 2기, 백엔드(클린코드&테스트코드) 과정에 참여하고 있습니다.이번 2주차에는 코드를 다듬을 때 기억하면 좋은 스킬과 팁을 배우고, 강사님께서 제공해주신 예제 프로젝트를 직접 리팩토링 해보는 시간을 가졌습니다.강의 링크: Readable Code: 읽기 좋은 코드를 작성하는 사고법  [학습내용 핵심 요약]자세한 설명이나 예제 및 코드는 강의 저작권 보호를 위해 기재하지 않았습니다. 추상주석의 양면성: 코드 품질 저하 vs. 필요한 정보 전달좋은 주석: 의사 결정 히스토리, 코드로 표현 불가능한 정보 전달주석 관리: 자주 변하는 정보 지양, 코드 변경 시 주석 업데이트변수/메서드 나열: 사용 순서, 중요도, 그룹화로 의도 전달패키지 구조: 문맥 정보 제공, 적절한 분할로 관리 용이성 확보패키지 변경: 같은 프로젝트에서 작업하는 팀과 합의 후 변경하는 것이 중요하다.(초기 설계의 중요성)객체 지향 설계로 인해, 버그 수정 용이성이 증가할 수 있다.재귀 알고리즘을 사용할때는 Stack Overflow 에러를 주의해야 한다. 이를 피하기 위해, Stack 자료 구조를 사용할 수 있다.IDE 활용: 코드 포맷팅, 품질 체크 도구(Sonarlint), .editorconfig 사용코드 스타일: 팀과 합의된 기준을 지키는 것이 중요하고, 지속적인 개선을 해나가는 것이 중요하다. 공개/비공개 메서드 배치: 객체의 기능 드러내기메서드 그룹화: 중요도, 종류별 배치로 일관성 유지스택 메모리 이해: 재귀 함수 사용 시 주의점코드 품질 관리를 위해서는 지속적인 리팩토링과 개선에 관심을 갖는 것이 중요하다. 기억하면 좋은 조언들능동적 읽기: 복잡한 코드 이해를 위한 리팩토링 접근법코드 이해 방법: 단락 구분, 추상화, 주석 활용 코드 읽기 목표: 도메인 지식 증진, 작성자 의도 파악오버 엔지니어링: 불필요한 복잡성 증가, 유지보수 어려움인터페이스 남용: 구현체가 하나일 때는 인터페이스를 남용하지 않도록 주의가 필요함이른 추상화 위험: 복잡도 증가, 의도 파악 어려움은탄환은 없다: 클린 코드도 만능 해결책이 아님실무 현실: 품질과 빠른 결과물 사이의 균형클린 코드 사고: 미래 수정 가능성 고려한 코드 작성적정 기술: 상황에 맞는 방법론 적용 중요성도구 사용 능력: 극한 사용과 적절한 제한에 대한 이해 필요적정 수준 파악을 위한 경험 축적을 위해, 클린 코드를 극단적으로 시도해보는 경험이 도움이 된다. 지속적 학습: 다양한 상황 경험을 통한 판단력 향상[후기]강의를 따라하기 전에, 혼자서 리팩토링 하며 고민해보는 시간을 가져본 다음 강사님의 강의를 따라가는 과정이 매우 효과적인 학습법으로 느껴져서 좋았던 시간이었습니다. 리팩토링 할 때 제가 놓친 부분들이 너무 많았던 것 같아 아쉽기도 했지만, 현재 내 실력을 보다 객관적인 관점으로 체감할 수 있었던 시간이라 더 좋았던 것 같습니다.이번 주차에서 배운 내용을 토대로 느낀점은 다음과 같습니다.내가 중요하게 생각했던 도메인의 추상 포인트와 다른 사람이 생각하는 포인트는 매번 다를 수 있다.추상화에 정답은 없고, 현재 시점에 적용할 수 있는 최선의 방법을 찾아내는 것이 그 시기에 할 수 있는 가장 최선의 방법이다.위의 두가지 내용은 계속 머리속에 새기고 있으면 좋을 것 같다는 생각이 들었습니다.이상으로, 2주차 학습 내용 요약과 후기를 정리한 발자국 포스팅을 마칩니다.

백엔드워밍업클럽백엔드리팩터링클린코드테스트코드발자국회고

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

강의에 대한 리뷰지난 주에 이어서 Readable Code를 진행했다.코드 다듬기, 리팩토링 연습, 기억하기 좋은 조언들, Outro 섹션을 진행하였고 Readable Code 강의는 마무리 되었다.저번 주 학습한 내용은 Readable Code를 어떻게 구현하는 가에 가까웠다면, 이번 주 학습 내용은 잘 정리된 Readable Code를 좀 더 정갈하게 정리해보자. 라는 느낌이었다.예를 들면, 섹션 6. 코드 다듬기에서는 주석의 양면성과 메서드 나열 순서, 패키지 나누기 등을 학습한는데, 내 생각에 이 주제들은 구현보다는 정리이기 때문에 강사님도 코드 다듬기라고 한 것이라 추측한다.강의 내용주석이 많다는 것은 비즈니스 요구사항을 어떤 방법으로든 코드에 잘 녹여내지 못했다는 뜻이다. 하지만 코드에는 도저히 녹여낼 수 없는 것들도 존재한다. 예를 들어 해당 코드에 대한 의사 결정 히스토리는 코드에 담아내기 어렵다. 나 역시 실무에서 레거시 코드를 봤을 때, "이렇게 하면 쉽고 깔끔할 것 같은데 왜 이렇게 복잡하고 어렵게 했지?" 라는 생각으로 리팩터링할 때가 종종있다. 이후에, 알고보니 그렇게 짜놓은 이유가 있는 코드였었다. (물론 10번 중에 한 두 번만..)클린코드와 주석에 대한 의견이 꽤 분분하다고 알고 있다. 모든 클래스와 메서드에 주석을 작성하기를 권장하는 사람들도 있는 반면, 이를 모두 코드에 담아내라는 사람들도 존재한다. 나는 아직 경험이 적기 때문에 정답은 잘 모르겠다. 하지만 자바, 스프링 등 구현된 소스를 보면 클래스 단위에 주석이 작성되어 있는 것이 좀 더 공부하기 좋았다. 주석은 상황, 팀 컨벤션 등에 따라 선택하면 될 것 같다. 또, 메서드 나열 순서에 대해서도 배웠는데, 이건 정답이라기 보다 뭐랄까... 좋은 습관 정도라고 생각한다. 생각해보면, 메서드의 순서는 그 순서를 알고 있는 사람에겐 Readable 하지만, 모르는 사람에겐 아무 도움도 되지 않는다. 어찌됐든 혼자 하든 같이 하든 메서드 나열 순서에 대한 컨벤션이 있으면 Readable 하겠다 라는 생각은 한다.  다음은, 기억하면 좋은 조언들 섹션에 대해 얘기해보려 한다.그 중, 오버 엔지니어링과 은탄환은 없다 섹션을 감명깊게 봤다. 최근에 내가 시도하려했던 사이드 프로젝트가 오버 엔지니어링으로 흐지부지 된 경험이 있다. 정확히 말하자면 오버 엔지니어링을 시도하다가 흐지부지 됐다고 하자. 클린코드나 Readable Code에 대한 건 아니고, 개발 하기도 전에 이것저것 (Sonarqube, Jacoco, Jenkins, Spring security...) 다 붙여놓은 다음에 개발하려다가 개발을 제대로 해보지도 못하고 지쳐서 흐지부지 되어버렸다. 작동하는 작은 단위부터 하나씩 쌓아올려야 한다는 점을 다시 되새겼다. 테스트 코드 작성에 대한 강의를 듣고 다시 시도해볼 것이다. 미션이번 주는 미션이 하나밖에 없었다.스터디카페 키오스크에 대한 코드를 지금까지 배운 것을 토대로 리팩터링하는 것이었다.가장 어려웠던 것은 추상화 레벨에 대한 것이다. 아직 추상화 레벨을 맞추는 게 어렵다. 추상화 레벨의 기준이라는 것도 잡기 어렵다. 이 부분은 꾸준히 생각하며 개발하다 보면 코드에 대한 사고가 변할 거라 생각한다. 회고나는 무엇이든 진득하게 못하는 것 같다. 다음 할 일이 있으면 그것을 위해 이전에 하던 것을 최대한 빠르게 마무리하고 넘어가려는 성향이 있다. 이런 성격만 바꾸면 조금 더 좋은 사람, 더 좋은 개발자가 되리라고 확신하는데 알면서도 그게 잘 안 된다. 이 부분을 바꿀 수 있는 방법에 대해 좀 더 고민해보고 알아봐야겠다.

허수빈

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

해당 글은 [인프런 워밍업 클럽 2기 클린 코드 & 테스트 코드]에 참가하여 박우빈님의 <Readable Code: 읽기 좋은 코드를 작성하는 사고법> 강의를 듣고 작성한 글입니다. ✍ 이번주 강의 요약객체 지향 적용하기상속은 부모와 자식 간의 결합이 높다. -> 확장과 변경이 어려움조합과 인터페이스를 사용하자!VO는 불변성, 동등성, 유효성 검증이 필수일급 컬렉션은 컬렉션을 객체로 다룬다.컬렉션만을 유일하게 변수로 가진다.가공 로직을 추가할 수 있음!일급 시민과 유사Enum은 상태와 행위를 한번에 정의한다.  코드 다듬기대부분 코드로 풀어내고, 그럼에도 전달해야 할 정보가 있을 때 주석을 쓰자!변수는 사용 순서로, 메서드는 public, private 순서로.상태 변경 -> 판별 -> 조회 순 StudyCafe 리팩토링!추상화 레벨 잘 맞춰주기무지성 getter, setter NO! 이왕이면 객체에 메시지를 보내는 형태로 해결하자.컬렉션이 의미가 있고, 가공 로직이 필요한 경우 일급 컬렉션을 쓰자!외부에서 데이터를 가져올 때,방법에 대해 초점을 맞추지 않는다. (기존에는 파일을 읽는 행위가 클래스로 만들어져 있었음)어떤 데이터가 필요한 지에 대한 추상을 적용하자! (Provider(규격)를 두고 필요한 것(구현체)을 제공.) 기억하면 좋은 조언들어떤 코드를 이해하려고 할 때, 모든 방법을 동원하자. -> 도메인 지식을 늘리고 선대의 의도 파악하는 것이 중요!완벽한 기술은 없다! 🏃 미션Day7다양한 리팩토링 조건이 있었는데 잘... 안됐다.. 중복을 제거하고 추상화 레벨을 맞추는 것만 해보려고 노력했다.일단 run()에 모든 로직이 모여 있어서 공동 로직을 추출했다.이용권 목록 파일 읽어와서 선택한 이용권 타입에 맞는 이용권 리스트 반환이용권 리스트 출력이용권 선택public void run() { try { outputHandler.showWelcomeMessage(); outputHandler.showAnnouncement(); //이용권 타입 선택 outputHandler.askPassTypeSelection(); StudyCafePassType studyCafePassType = inputHandler.getPassTypeSelectingUserAction(); //이용권 목록 파일 읽어오기 StudyCafeFileHandler studyCafeFileHandler = new StudyCafeFileHandler(); List<StudyCafePass> studyCafePasses = studyCafeFileHandler.readStudyCafePasses(); //이용권 타입에 맞는 이용권 리스트 반환 List<StudyCafePass> filteredPasses = filterStudyCafePassesByType(studyCafePasses, studyCafePassType); //이용권 선택 outputHandler.showPassListForSelection(filteredPasses); StudyCafePass selectedPass = inputHandler.getSelectPass(filteredPasses); if (studyCafePassType == StudyCafePassType.HOURLY) { outputHandler.showPassOrderSummary(selectedPass, null); } else if (studyCafePassType == StudyCafePassType.WEEKLY) { outputHandler.showPassOrderSummary(selectedPass, null); } else if (studyCafePassType == StudyCafePassType.FIXED) { List<StudyCafeLockerPass> lockerPasses = studyCafeFileHandler.readLockerPasses(); StudyCafeLockerPass lockerPass = lockerPasses.stream() .filter(option -> option.getPassType() == selectedPass.getPassType() && option.getDuration() == selectedPass.getDuration() ) .findFirst() .orElse(null); boolean lockerSelection = false; if (lockerPass != null) { outputHandler.askLockerPass(lockerPass); lockerSelection = inputHandler.getLockerSelection(); } if (lockerSelection) { outputHandler.showPassOrderSummary(selectedPass, lockerPass); } else { outputHandler.showPassOrderSummary(selectedPass, null); } } } catch (AppException e) { outputHandler.showSimpleMessage(e.getMessage()); } catch (Exception e) { outputHandler.showSimpleMessage("알 수 없는 오류가 발생했습니다."); } }스터디 카페 타입에 맞는 이용권 리스트를 반환하는 로직은 메서드로 추출했다.private List<StudyCafePass> filterStudyCafePassesByType(List<StudyCafePass> studyCafePasses, StudyCafePassType studyCafePassType) { return studyCafePasses.stream() .filter(studyCafePass -> studyCafePass.getPassType() == studyCafePassType) .toList(); }그리고 현재 분기문이 스터디 카페 타입에 따라 나눠지는데,공통적인 부분 : 선택한 이용권 내역 출력고정권일 때, 사물함을 추가적으로 받는다.잠깐 출력 메서드인 outputHandler.showPassOrderSummary()을 알아보자!public void showPassOrderSummary(StudyCafePass selectedPass, StudyCafeLockerPass lockerPass) { .... }이 메서드는 선택한 이용권과 사물함 여부를 인수로 받는다.즉, 공통적으로 이 메서드를 쓰고시간권, 주간권일 때는 lockerPass를 null고정권이고 사물함을 쓸 때만 lockerPass 값을 넣자.run()public void run() { try { outputHandler.showWelcomeMessage(); outputHandler.showAnnouncement(); //이용권 타입 선택 outputHandler.askPassTypeSelection(); StudyCafePassType studyCafePassType = inputHandler.getPassTypeSelectingUserAction(); //이용권 목록 파일 읽어오기 StudyCafeFileHandler studyCafeFileHandler = new StudyCafeFileHandler(); List<StudyCafePass> studyCafePasses = studyCafeFileHandler.readStudyCafePasses(); //이용권 타입에 맞는 이용권 리스트 반환 List<StudyCafePass> filteredPasses = filterStudyCafePassesByType(studyCafePasses, studyCafePassType); //이용권 선택 outputHandler.showPassListForSelection(filteredPasses); StudyCafePass selectedPass = inputHandler.getSelectPass(filteredPasses); //이용권 내역 출력 showPassOrderDetails(selectedPass); } catch (AppException e) { outputHandler.showSimpleMessage(e.getMessage()); } catch (Exception e) { outputHandler.showSimpleMessage("알 수 없는 오류가 발생했습니다."); } }showPassOrderDetails()private void showPassOrderDetails(StudyCafePass selectedPass) { StudyCafeLockerPass lockerPass = getLockerPassIfFixed(selectedPass); outputHandler.showPassOrderSummary(selectedPass, lockerPass); }getLockerPassIfFixed()private StudyCafeLockerPass getLockerPassIfFixed(StudyCafePass selectedPass) { if (selectedPass.getPassType() == StudyCafePassType.FIXED) { //사물함 목록 파일 읽기 List<StudyCafeLockerPass> lockerPasses = studyCafeFileHandler.readLockerPasses(); //고정권 타입 조건에 맞는 사물함 반환 StudyCafeLockerPass lockerPass = lockerPasses.stream() .filter(option -> option.getPassType() == selectedPass.getPassType() && option.getDuration() == selectedPass.getDuration()) .findFirst() .orElse(null); //사물함이 있다면, 사물함 선택 여부를 입력 받기 boolean lockerSelection = false; if (lockerPass != null) { outputHandler.askLockerPass(lockerPass); lockerSelection = inputHandler.getLockerSelection(); } //사물함을 선택한다면 if (lockerSelection) { return lockerPass; } else { return null; } } return null; }이용권 타입이 고정권일 때,사물함을 선택한다면 lockerPass 반환사물함을 선택하지 않는다면 null 반환이용권 타입이 고정권이 아니라면 null 반환getLockerPassIfFixed() 내부가 너무 복잡하므로 추상화를 하기로 했다.FileHandler는 변하지 않고 공통적으로 쓰이므로 맨 위로 private final로 빼주었다.고정권 타입에 맞는 사물함을 반환하는 부분을 메서드로 추출사물함 선택 여부 메서드로 추출getLockerPassIfFixed()private StudyCafeLockerPass getLockerPassIfFixed(StudyCafePass selectedPass) { if (selectedPass.getPassType() == StudyCafePassType.FIXED) { //사물함 목록 파일 읽기 List<StudyCafeLockerPass> lockerPasses = studyCafeFileHandler.readLockerPasses(); //고정권 타입 조건에 맞는 사물함 반환 StudyCafeLockerPass filterdLockerPass = filterLockerPassByTypeAndDuration(lockerPasses, selectedPass); //사물함이 없다면, null 반환 if (filterdLockerPass == null) { return null; } //사물함을 선택한다면 if (askForLockerUsage(filterdLockerPass)) { return filterdLockerPass; } } return null; }filterLockerPassByTypeAndDuration()private static StudyCafeLockerPass filterLockerPassByTypeAndDuration(List<StudyCafeLockerPass> lockerPasses, StudyCafePass selectedPass) { return lockerPasses.stream() .filter(option -> option.getPassType() == selectedPass.getPassType() && option.getDuration() == selectedPass.getDuration()) .findFirst() .orElse(null); }askForLockerUsage()private boolean askForLockerUsage(StudyCafeLockerPass filterdLockerPass) { outputHandler.askLockerPass(filterdLockerPass); return inputHandler.getLockerSelection(); }그리고 run()에는 행위별로 나뉘어져 있으면 좋겠다고 생각해서안내메시지 출력이용권 타입 선택이용권 선택이용권 내역 출력으로 정리했다. public class StudyCafePassMachine { private final InputHandler inputHandler = new InputHandler(); private final OutputHandler outputHandler = new OutputHandler(); private final StudyCafeFileHandler studyCafeFileHandler = new StudyCafeFileHandler(); public void run() { try { //스터디 카페 안내 메시지 outputHandler.showWelcomeMessage(); outputHandler.showAnnouncement(); //이용권 타입 선택 StudyCafePassType studyCafePassType = getPassTypeFromUser(); //이용권 선택 StudyCafePass selectedPass = getPassFromUser(studyCafePassType); //이용권 내역 출력 showPassOrderDetails(selectedPass); } catch (AppException e) { outputHandler.showSimpleMessage(e.getMessage()); } catch (Exception e) { outputHandler.showSimpleMessage("알 수 없는 오류가 발생했습니다."); } } private StudyCafePassType getPassTypeFromUser() { outputHandler.askPassTypeSelection(); return inputHandler.getPassTypeSelectingUserAction(); } private StudyCafePass getPassFromUser(StudyCafePassType studyCafePassType) { List<StudyCafePass> studyCafePasses = studyCafeFileHandler.readStudyCafePasses(); List<StudyCafePass> filteredPasses = filterStudyCafePassesByType(studyCafePasses, studyCafePassType); outputHandler.showPassListForSelection(filteredPasses); return inputHandler.getSelectPass(filteredPasses); } private List<StudyCafePass> filterStudyCafePassesByType(List<StudyCafePass> studyCafePasses, StudyCafePassType studyCafePassType) { return studyCafePasses.stream() .filter(studyCafePass -> studyCafePass.getPassType() == studyCafePassType) .toList(); } private void showPassOrderDetails(StudyCafePass selectedPass) { StudyCafeLockerPass lockerPass = getLockerPassIfFixed(selectedPass); outputHandler.showPassOrderSummary(selectedPass, lockerPass); } private StudyCafeLockerPass getLockerPassIfFixed(StudyCafePass selectedPass) { if (selectedPass.getPassType() == StudyCafePassType.FIXED) { List<StudyCafeLockerPass> lockerPasses = studyCafeFileHandler.readLockerPasses(); StudyCafeLockerPass filterdLockerPass = filterLockerPassByTypeAndDuration(lockerPasses, selectedPass); if (filterdLockerPass == null) { return null; } if (askForLockerUsage(filterdLockerPass)) { return filterdLockerPass; } } return null; } private static StudyCafeLockerPass filterLockerPassByTypeAndDuration(List<StudyCafeLockerPass> lockerPasses, StudyCafePass selectedPass) { return lockerPasses.stream() .filter(option -> option.getPassType() == selectedPass.getPassType() && option.getDuration() == selectedPass.getDuration()) .findFirst() .orElse(null); } private boolean askForLockerUsage(StudyCafeLockerPass filterdLockerPass) { outputHandler.askLockerPass(filterdLockerPass); return inputHandler.getLockerSelection(); } }🗒️ 회고드디어 완강했다! 그런데 반쯤은 이해 못해서 무조건 복습해야 할 것 같다..ㅎㅎ미션을 하면서 너무 헤매서 괴로웠다! ㅜㅜ 나는 지금껏 뭘 들은거지... 싶었다. 강의자님이 하신 리팩토링을 보면서 내 코드가 너무 바보같고 한심했다..... 꼭 복습해서 완벽히 내 것으로 만들고 싶다!아무튼 이번 클린 코드 강의를 통해서 지금까지 내가 작성했던 코드가 얼마나 엉망진창이었는지 알게 됐다..... 그래도 내가 지금 제대로 하는 건지, 이게 맞는 건지 항상 의문이 있었는데 나아갈 방향이 생겨서 좋다.다음주부터 시작하는 테스트 코드도 화이팅! 밀리지 않고 열심히 듣기~!!🥰🥰🥰🥰

웹 개발워밍업클럽

허수빈

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

해당 글은 [인프런 워밍업 클럽 2기 클린 코드 & 테스트 코드]에 참가하여 박우빈님의 <Readable Code: 읽기 좋은 코드를 작성하는 사고법> 강의를 듣고 작성한 글입니다.✍ 이번주 강의 요약추상외부 세계는 추상화 레벨이 높고, 내부 세계는 추상화 레벨이 낮다.추상화 레벨이 높다! -> 좀 더 포괄적임.추상화 레벨이 낮다! -> 좀 더 구체적임.추상화 레벨은 주변과 맞춰줘야 한다. 논리, 사고의 흐름else 대신 early return 을 쓰자! -> 이전 것들을 신경쓰지 않고 사고의 깊이를 줄일 수 있음.부정어 대신 의미가 명확한 메소드를 쓰자! 객체 지향 패러다임객체 지향이란 곧, 관심사를 분리하여 캡슐화를 잘하는 것.solid 규칙을 명시하자.결합도를 낮추고 응집도를 높이기 🏃 미션Day2일상생활에서 쉽게 찾을 수 있는 추상과 구체를 생각하려고 했다. '음식을 먹다' 라는 추상을 음식을 입에 넣는 과정, 씹는 과정, 삼키는 과정으로 구체화하여 적으려고 했다. Day4먼저 해당 메서드의 요구사항이 무엇인지 파악하려고 했다. 이 주문 검증 메서드의 요구사항은 아래와 같았다.주문 항목이 0 이상이어야 한다.총 주문 금액이 0 이상이어야 한다.주문을 하는 사용자 정보가 있어야 한다.기존 메서드는 2번째 요구사항과 3번째 요구사항이 합쳐서 구현되어 있었으므로 일단 각 요구사항을 기능별로 분리하였다. 그 다음, early return을 이용하여 불필요한 else 메서드를 지웠다. public boolean validateOrder(Order order) { if (order.getItems().size() == 0) { log.info("주문 항목이 없습니다."); return false; } if (!order.hasCustomerInfo()) { log.info("사용자 정보가 없습니다."); return false; } if (!(order.getTotalPrice() > 0)) { log.info("올바르지 않은 총 가격입니다."); return false; } return true; } 가독성을 위해 각 기능별로 메서드를 추출했다.public boolean validateOrder(Order order) { if (hasItems(order)) return false; if (hasCustomerInfo(order)) return false; if (isTotalPriceInvalid(order)) return false; return true; } private static boolean isTotalPriceInvalid(Order order) { if (!(order.getTotalPrice() > 0)) { log.info("올바르지 않은 총 가격입니다."); return true; } return false; } private static boolean hasCustomerInfo(Order order) { if (!order.hasCustomerInfo()) { log.info("사용자 정보가 없습니다."); return true; } return false; } private static boolean hasItems(Order order) { if (order.getItems().size() == 0) { log.info("주문 항목이 없습니다."); return true; } return false; } 부정어를 지우고 각각의 기능을 만족했을 때만 true가 되도록 했다.public boolean validateOrder(Order order) { return hasItems(order) && hasCustomerInfo(order) && isTotalPriceInvalid(order); } private static boolean hasItems(Order order) { if (order.getItems().size() > 0) { return true; } log.info("주문 항목이 없습니다."); return false; } private static boolean isTotalPriceInvalid(Order order) { if (order.getTotalPrice() > 0) { return true; } log.info("올바르지 않은 총 가격입니다."); return false; } private static boolean hasCustomerInfo(Order order) { if (order.hasCustomerInfo()) { return true; } log.info("사용자 정보가 없습니다."); return false; } 🗒️ 회고현재 공채 시즌이라 진도를 빨리 나가지 못해서 아쉬웠다.그래도 어찌저찌 미션도 제출하고 회고도 적어서 뿌듯했다.첫 번째 미션은 쉬웠는데 두 번째 리팩토링 미션이 꽤 오래 걸렸다. 대신 좋은 코드, 읽기 쉬운 코드... 가독성이 좋은 코드에 대해 오랫동안 생각할 수 있어서 좋았다. 내 코드가 맞는 건지는 잘 모르겠지만ㅎㅎ.. 앞으로 강의를 들으면서 더 깊고 넓게 생각할 수 있는 능력을 기르고 싶다!  

웹 개발워밍업클럽

양치잘하기

코틀린 백엔드 프로젝트 2기 - 2주차

이번주차는 섹션 3-7~ 섹션 4-5까지의 수업이 있었습니다. 섹션 3데이터베이스 초기화, 리포지토리 개발, 테스트 코드 작성, 리포지토리의 성능 개선을 학습 하였습니다. 데이터베이스 초기화데이터 베이스 초기화는 DataInitializer 클래스에서 진행을 해주었습니다.해당 클래스를 빈으로 등록해주는 어노테이션인 @Component 를 사용 하였습니다. 생성자 주입을 통해 리포지토리 인터페이스를 주입 받고 @PostCunstruct 어노테이션을 이용한 메서드를 통해서 본격적인 데이터베이스 초기화를 실행하였습니다.@PostConstruct fun initializeData(){ }엔티티 클래스들을 생성자를 통해 초기화 하여 생성한 뒤 주입받은 리포지토리의 기본적인 CRUD기능의 메서드를 이용하여 데이터베이스에 저장하였고 1:N 관계를 가지는 엔티티들은 각 필드의 리스트에 해당 엔티티들을 초기화 해주었습니다.@PostConstruct fun initializeData(){ ... val experience1 = Experience( title = "캣홀릭대학교(CatHolic Univ.)", description = "컴퓨터공학 전공", startYear = 2018, startMonth = 9, endYear = 2022, endMonth = 8, isActive = true, ) experience1.addDetails( mutableListOf( ExperienceDetail(content = "GPA 4.3/4.5", isActive = true), ExperienceDetail(content = "소프트웨어 연구 학회 활동", isActive = true) ) ) val experience2 = Experience( title = "캣카오", description = "소셜 서비스팀 백엔드 개발", startYear = 2022, startMonth = 9, endYear = null, endMonth = null, isActive = true, ) experience2.addDetails( mutableListOf( ExperienceDetail(content = "유기묘 위치 공유 서비스 게발", isActive = true), ExperienceDetail(content = "신입 교육 프로그램 우수상", isActive = true) ) ) experienceRepository.saveAll(mutableListOf(experience1, experience2)) ... }이 작업들을 통해서 데이터베이스 초기화를 진행해 주었습니다. 리포지토리 개발스프링에서 제공하는 Spring Data JPA는 인터페이스를 상속하는것만으로 기본적인 CRUD 기능을 제공해줍니다.또한 메서드 이름을 기반으로 하는 쿼리 생성 기능도 있습니다.(커스텀 메서드) findBy, deleteBy, countBy같은 키워드 뒤에 필드들을 붙여주어 JPA가 자동으로 쿼리를 생성해줍니다.// 커스텀 메서드 interface ExperienceRepository : JpaRepository<Experience, Long>{ @Query("select e from Experience e left join fetch e.details where e.isActive = :isActive") fun findAllByIsActive(isActive : Boolean) :List<Experience> @Query("select e from Experience e left join fetch e.details where e.id = :id") override fun findById(id: Long): Optional<Experience> }@QuerySpring Data JPA에서 사용자 정의 JPQL (Java Persistence Query Language) 또는 네이티브 SQL 쿼리를 작성할 수 있게 해주는 어노테이션입니다.jqpl에서 fetchJoin을 사용할 수 있는데 이 기능을 통해 지연로딩으로 받아오는 연관된 엔티티를 한번에 즉시로딩을 할 수 있으며 n+1문제를 해결할 수 있습니다. N+1문제대표적인 JPA의 단점으로 연관관계를 가진 두 개의 부모-자식 테이블에서 부모를 호출할 때 JPA는 엔티티의 연관관계를 바탕으로 조회해온 데이터의 갯수만큼 부모에 매핑된 자식 테이블의 데이터를 조회하기 위한 쿼리를 생성합니다.예를 들어 프로젝트 - 프로젝트 멤버 의 관계에서 프로젝트가 3개 존재할 때 프로젝트를 조회하면 1번의 쿼리로 3개의 프로젝트가 조회 됩니다. 이때 각 조회된 프로젝트의 프로젝트 멤버를 1회 더 조회합니다. 프로젝트가 1회 조회될 때 3개의 프로젝트가 조회되었으므로 총 3번의 조회를 더 하게 됩니다.이를 N+1 문제(1+N 문제)라고 합니다. 리포지토리 성능 개선N+1문제가 발생하는 리포지토리의 성능을 개선하기위해선 엔티티에서 연관관계 매핑 시 설정가능한 FetchType의 종류에 대하여 알아야 합니다.fetch = FetchType.LAZY fetch = FetchType.EAGERLazy : 지연로딩 방법 부모 - 자식관계 엔티티에서 부모를조회하여도 자식을 호출하기 전까지는 조회하지 않습니다.Eager : 부모 조회 즉시 자식 조회N+1문제를 해결하기 위해 LAZY 타입을 사용합니다. 그 후 자식이 필요하면 @Query에 FetchJoin을 사용하여 부모와 자식의 데이터를 같이 조회하도록 할 수 있습니다. 테스트 코드테스트 코드 작성은 운영 전 사이드 이펙트 감지를 위한 매우 중요한 기능입니다.어노테이션@DataJpaTestJPA 관련 테스트 설정 제공, 내장 데이터 베이스 설정, @Entity, @Repository가 부여된 클래스들의 테스트 환경을 설정@TestInstance테스트 인스턴스의 라이프 사이클을 지정,Junit5는 각 테스트 메서드마다 새로운 인스턴스를 생성@BeforeAll테스트 클래스내의 모든 @Test메소드 실행전 한 번 실행되도록 함 테스트 데이터 초기화를 할 때 유용 @Test테스트 메소드 지정@Autowired필드 주입 방식 사용, 테스트 코드에서는 필드주입을 사용 @DisplayName테스트 실행 후 Run 창에서 실행한 테스트의 이름을 설정테스트코드는 개발자가 더미데이터를 생성한 후 해당 더미데이터가 비즈니스 로직에 알맞게 들어갔는지 검증하면서 테스트를 진행합니다. 섹션 4presentation패키지에 Controller와 DTO, Repository를 생성하였습니다. Controller레이어드 아키텍처에서 사용자의 요청이 진입하는 엔트리포인트로 실질적인 처리를 하는 service레이어로 넘겨주고 응답을 반환하는 기능을 합니다. @RequestMapping, @GetMapping 등 의 기능을 통해 진입 api를 설정합니다.@Controller, @RestController 어노테이션이 있습니다.@Controllerreturn 되는 문자열같은 html파일을 찾아 클라이언트에 응답모델에 값을 담아두면 해당 html에서 모델에 담긴 값을 꺼내어 사용이 가능@RestController@Response와 @Controller가 합쳐진 기능 CSR방식의 웹 개발, 앱 개발, 데이터의 처리만 담당하는 API 개발 시 사용return값은 그대로 HTTP 응답 메시지 바디에 들어가며 여러 타입으로 변환하여 반환 가능Service컨트롤러에서 받은 데이터를 비즈니스 로직에 맞게 처리하는 영역이며 필요에 따라 Repositry를 호출합니다. Repository데이터베이스와 상호작용 하는 영역 DTODTO(Data Transfer Object)는 데이터 전송 객체로, 주로 소프트웨어 애플리케이션에서 데이터의 전송 및 관리를 간편하게 하기 위해 사용되는 객체입니다. 데이터를 그룹화하여 전송할 때 유용합니다.데이터 전송을 단순화하고 최적화하기 위함으로 비즈니스 로직이 없는 단순한 데이터 구조로 시스템간의 데이터 교환을 간소화 할 수 있다. 리포지토리 개발이번 강의에서 리포지토리는 퍼사드 패턴을 적용하여 개발을 하였습니다. 퍼사드 패턴은 복잡한 여러 기능을 단순화해주는 소프트웨어 디자인 패턴입니다.@Repository class PresentationRepository ( private val achievementRepository: AchievementRepository, private val introductionRepository: IntroductionRepository, private val linkRepository: LinkRepository, private val skillRepository: SkillRepository, private val projectRepository: ProjectRepository, private val experienceRepository: ExperienceRepository, ){ fun getActiveAchievements(): List<Achievement>{ return achievementRepository.findAllByIsActive(true) } fun getActiveExperiences(): List<Experience>{ return experienceRepository.findAllByIsActive(true) } fun getActiveAIntroductions(): List<Introduction>{ return introductionRepository.findAllByIsActive(true) } fun getActiveLinks(): List<Link>{ return linkRepository.findAllByIsActive(true) } fun getActiveSkills(): List<Skill>{ return skillRepository.findAllByIsActive(true) } fun getActiveProjects(): List<Project>{ return projectRepository.findAllByIsActive(true) } } 생성자 주입을 통해 필드로 리포지토리들을 보유하고 있으며 메서드를 구현하여 각 리포지토리에서 활성화된 객체를 가지고 옵니다. 서비스 개발@Transactional 어노테이션을 이용하여 트랜잭션을 시작합니다.reagOnly일기전용 트랜잭션으로 설정, JPA를 사용시 더티체킹을 하지 않음rollbackFor어떤 예외시 롤백할지 설정isolation격리 수준을 정의@Service class PresentationService( private val presentationRepository: PresentationRepository ) { @Transactional(readOnly = true) fun getIntroductions(): List<IntroductionDTO>{ val introductions = presentationRepository.getActiveAIntroductions() return introductions.map { IntroductionDTO(it) } } @Transactional(readOnly = true) fun getLinks(): List<LinkDTO>{ val links = presentationRepository.getActiveLinks() return links.map { LinkDTO(it) } } @Transactional(readOnly = true) fun getResume(): ResumeDTO{ val experiences = presentationRepository.getActiveExperiences() val achievements = presentationRepository.getActiveAchievements() val skills = presentationRepository.getActiveSkills() return ResumeDTO( experiences = experiences, achievements = achievements, skills = skills, ) } @Transactional(readOnly = true) fun getProjects(): List<ProjectDTO>{ val projects = presentationRepository.getActiveProjects() return projects.map { ProjectDTO(it) } } }생성자 주입으로 리포지토리를 주입받아 서비스 레이어에서 리포지토리를 호출하여 비즈니스 로직을 실행하고 있습니다 서비스 테스트 코드 작성@ExtendWith : Junit5에서 테스트 확장을 지원 @InjectionMocks, : Mockito에서 테스트 대상이 되는 클래스에 인서턴스 주입을 위해 사용@Mock : Mockito에서 Mock 객체를 생성 Mock는 실제 객체를 대체하는 가짜 객체를 의미합니다.테스트에서 특정 객체의 모의 객체를 사용하게 해줍니다.@Mock을 이용하여 모의 객체를 생성하고 @InjectionMocks를 사용하여 주입된 모의 객체를 사용하는 객체를 생성 합니다.@AutoWired를 사용하지 않고 의존성 주입을 받으면서 실제 DB에 의존하지 않고 테스트를 진행할 수 있습니다

채널톡 아이콘