블로그

ea04638

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

3주차 강의만 간신히 들었다. Day 11테스트 주도 개발, React TDD 를 통한 간단한 앱 생성 및 배포 강의요약TDD실제 코드를 작성하지 전에 테스트 코드를 작성하고, 테스트 코드를 패스 할 수 있는 실제 코드를 작성하는 것.그런데 여기까지 들으면 원래 그렇게 하지 않나? 라는 생각이 들었는데. 그것과는 별도같다.React Testing Library DOM testing library이라는 DOM Node를 테스트하기 위한 솔루션 위에 react를 위한 테스트를 하는 api를 추가한 것이 React Testing Library.Jest페이스북이 만들어낸 테스팅 프레임 워크. 최소한의 설정으로 동작, Test Case 를 만들어서 코드가 잘 작동하는지 확인한다. (unit단위 테스트를 위해 진행)Prettier들여쓰기 값, 따옴표의 종류 등을 정리해서 보기 좋게 해주는 코드 포맷터ESLint 개발자들이 규칙성을 갖고 코드를 깔끔하게 작성할 수 있도록 도와주는 라이브러리. 문법 오류를 알려주고, 코드 작성에 가이드를 제시한다. Day 12Next.js 와 TypeScript, 리액트 version 18강의요약Next.jsReact의 서버사이드 렌더링을 쉽게 구현할 수 있게하는 프레임워크이며, 라우팅 기능을 쉽게 만들어준다.리액트는 SPA 이기 떄문에 클라이언트 서버 렌더링(빈 html을 가져와 js 파일을 해석하며 화면 구성)에는 편리하지만, 빈 html을 가져오는 방식 때문에 SEO 등이 좋지 못한데, next js가 pre-rendering을 통해 미리 렌더링한 완성된 html을 가져와 이 문제를 보완한다.TypsScript자바스크립트에 타입을 부여한 언어. (자바스크립트의 확정 언어)브라우저에서 실행하려면 js와 달리 변환(컴파일) 이 필요하다.등장배경js는 원래 클라이언측 언어였는데, Node.js의 개발로 서버측에서도 활용하게 되면서 코드의 유지, 관리, 재사용을 용이하게 하기 위해 ts가 만들어지게 되었다. 타입 검사, 컴파일 오류 검사 등의 기능 또한 보완하였다.타입 종류string, number, booleadn, null, undefined, symbold 이외에도 존재한다.any : 타입실드를 아예 삭제시킴으로써 제한을 없앤다.unknown : 모든 타입 사용 가능Enum : 열거형, 숫자값 외에 문자열도 가능.Union : 할당될 값의 타입이 확실하지 않은 경우, or 연산자를 사용하여 union 타입을 작성. 변수나 함수 매개변수가 여러 타입 중 하나를 가질 수 있음 Void : 값이 없음. 반환값이 없을 때 주로 사용된다.Never :  절대로 사용 안할거라는 의미.   과제 퀴즈앱조건4지선답 문제를 제시, 유저가 선택한 문제가 정답인지 오답인지 알려준다.문제마다 카테고리가 있으며, state 메뉴에서는 카테고리를 골라 문제를 풀 수 있도록 한다.quiz 메뉴로 이동 시, 카테고리 선택> 카테고리에 해당하는 문제를 시험칠 수 있다.이 경우, 문제를 푼 뒤 바로 답을 알려주지 않고, 문제를 모두 푼 뒤 결과를 채점해준다.진행next js를 통해 프로젝트 생성라우팅 기능을 이용해 각 메뉴 페이지 생성data 폴더를 생성하여 공통으로 사용될 문제의 정보를 담을 파일 생성fuc 폴더를 생성하여 정답판별 등 공통으로 사용될 함수 작성각 페이지에서 화면에 그릴 요소를 정하는 것은 변수의 t/f로 최대한 단순하게 작성.시간이 없어서 최대한 단순하게 하려고 노력했다.  Day 13리덕스, 도커를 이용한 리액트 실행, 리액트 19 나는 리덕스가 너무 어렵다. 뭔가 활용법이 와닿지 않는 것들이 유독 난해하고 이해하기 어렵게 느껴지는 것 같은데, 리덕스 또한 그런 종류다... 아마 리덕스를 쓸정도로 복잡하거나 데이터가 많이 필요한 프로젝트를 아직 안해봤고, 사실 그 이전에 데이터 관리를 할 일이 그렇게 많지 않았어서인듯. 강의요약리덕스js 애플리케이션을 위한 상태 관리 라이브러리 데이터가 기존의 방식으로는 관리 불가능할 정도로 많고 복잡해졌을 때, 값을 이동하는 방식을 단순화하고 관리의 용이성을 위해 store 등을 이용해 관리한다.props없이도 component간 state공유를 지원한다.redux store에 state를 저장하고, 각 component들이 store에 접근하여 state를 사용한다. 도커프로그램 다운 과정을 간략하게 만들어준다.컨테이너 기반의 오픈소스 가상화 플랫폼, 생태계 리액트19react compoiler, use() hook, actions, use client, use server, useForm status, useActionState, useOptimistic, Meta tages, React SErver Component등 비교적 신기능을 학습한다.use() 라던가 Meta Tage, use Client/use Server 등 어쩌다보니 오류 고치다가 미리 써본 기능들도 있다. 알아두면 도움 될 것 같은데, 아마 까먹고있다가 다음에 필요하게되면 다시 검색해서 쓰겠지.  후기2주차 후반부터 회사일이 뭔가... 정신없게 돌아가면서 도저히 집에 와서도 과제를 할 만한 상황이 아니었다.본격적으로 공부하고, 본격적으로 집중했어야 할 리액트 리덕스 등 내가 어려워하는 파트였는데.후반부 집중력이 많이 흐트러지고 과제도 간신히 한 개 한게 많이 아쉽다.워밍업 클럽 기간이 끝나고 따로 공부해볼 예정이다.

프론트엔드인프런워밍업클럽

김진수

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

인프런 워밍업 클럽 2기 발자국 3주차1. 한 주의 정리이번 주차부터는 테스트 코드 학습이 시작됐다.테스트 코드는 뭔가 짜야지 짜야지 하면서, 처음 하려고 하면 뜬 구름 같은..?마치 헬스장을 PT 없이 처음 가는 기분일까..? 아무래도 유튜브나 이런걸 보고 할 순 있지만 뭔가처음가면 런닝머신만 뛰고 오기 부지기수이다.나에게 테스트 코드는 그런 느낌이었다. 혼자서 짜면 굉장히 얇은 층만 건들이다가 마는 것 같은..? 3주차에서는 일단 내가 건들이던 층까지라크게 다를 것은 없었지만 그럼에도 많은 것들을 배웠다. ✅ 섹션 2. 테스트는 왜 필요할까?테스트의 필요성에 대해 먼저 간략히 알려주는 섹션이다.여기서 말씀 주신 것들의 대부분이 공부를 하거나 일을 할 때 테스트 코드가 없는 경우에 느낀 불편함들이 있었다.이 섹션에서는 그러한 불편한 부분들 그리고 테스트 코드 자체로 발생할 수 있는 불편함들에 대해 정리 해주고 있다. ✅ 섹션 3. 단위 테스트가장 쉽게 접근할 수 있고 또 빠르게 테스트를 할 수 있는 단위 테스트에 대해 학습했다.단위 테스트를 작성하는 기법이나 프레임워크 등 그리고 어떤 것들을 테스트해야 하는 지 생각하는 방식 등을 알 수 있어 조금은 테스트에 있어 어색한 나에게 좋은 섹션이었다.특히 테스트하기 어려운 것과 쉬운 것을 구분하고 테스트하기 쉬운 구조로 변경하는 방법이 꽤 도움이 되었다.반면에 테스트하기 쉽게 프로덕트를 변경하는 것이 옳은 것인가? 에 대해 한번 고민하게 되었다 ✅ 섹션 4. Test Driven Development한 동안 엄청나게 많은 사람들을 괴롭힌 TDD이다강의에서 간략한 요약으론 프로덕션 코드보다 테스트 코드를 먼저 작성하여 테스트가 구현 과정을 주도하도록 하는 방법론RED : 실패하는 테스트 작성GREEN : (빠른 시간 내에) 테스트 통과 최소한의 코딩REFACTOR : 구현 코드 개선 테스트 통과 유지 위와 같이 안내해준다. 이 장에 대해선 좀 모호하다. 나는 TDD를 옹호하고 긍정적인 쪽은 아니다.그러나 테스트 코드는 중요하다고 생각한다. 프로덕트를 먼저 짜든 테스트를 먼저 짜든 중요한 것은 그 본질이라 생각하며나는 그 부분이 요구사항을 만족하며 최대한 꼼꼼한 기능 구현이라고 생각한다. ✅ 섹션 5. 테스트는 [ ] 다괄호 안의 내용을 말하는 것이 강의의 스포일까..? 두루뭉술하게 말하자면테스트 자체도 보기 쉽게 작성을 해야한다는 강의 내용이었다.@DisplayName을 명확히 작성한다든지 gwt나 gw&t 방식으로 구분지어 테스트를 작성하여 보기 쉽게 만들어 주는게 좋지 않을까요? 하는 관점이었다. ✅ 섹션 6. Spring & JPA 기반 테스트쓸 건 많은데 쓰기엔 좀.. 강의 내용을 너무 적나라하게 내보낼까봐 조심스럽다.그리고 나 또한 강의의 내용을 그대로 옮기는 회고를 선호하지 않기에.. 이 장에서는 Layerd Architecture에서 많이 활용되는 방식의 테스트 구조를 설명하고아랫단에서부터 올라가고 있다. 우선 이번 주차에선[6-2] Business Layer까지라 나눠서 작성하기도 애매하고.. 일단은 이 강의의 가장 핵심 부분이지 않나 싶다 섹션 6과 7은 그 밖에도 테스트 상황에서 발생할 수 있는 여러 문제 상황들을 상세하게 재연해주시므로 도움이 많이 되는 장이다. 2. 미션이번 주차에서는 테스트 코드를 작성하는 미션이 진행됐다.코드의 양이 꽤 많으므로 github 링크를 같이 첨부하며 어떤 식으로 접근 했는 지 설명하겠다. https://github.com/ckaanf/readable-code/tree/mission/day-12 ✅ 아래부터 차근 차근처음 테스트가 하나도 없는 코드를 받으면 숨이 턱..왜냐하면 테스트 상황에 대한 구조부터 하나하나 쌓아 올려가야 하기 때문이다.그나마 이 프로젝트는 Spring이 아니었기에 여러 Mock들이나 스프링 Bean에 의해 발생하는 문제는 없어서 다행이었다. 타고타고타고나는 일단 메인 비즈니스 로직에서 메서드를 계속 타고 최하단 까지 내려 가는 것으로 먼저 테스트 코드를 작성했다. 거기서 객체 수준의 테스트가 필요하다고 생각되면 작성을 하였고 또 역으로 그 객체를 사용하는 위치로 가서 테스트 코드를 작성했다. 미션은 테스트 코드 작성두 번째로는 테스트를 위한 프로덕트의 변경은 하지 않았다.그저 내가 생각하기에 필요하다고 생각되는 테스트 케이스는 작성하되 그것이 통과하도록 프로덕트를 변경하진 않았다. 이번 미션은 테스트 코드 작성이지 리팩토링이나 기능의 변경이 아니라고 생각했기 때문이다.그렇기에 실패하는 테스트도 존재하며, 그것은 의도된 것임을 밝힌다 최대한 독립적으로마지막으로는 최대한 독립적으로 짜려고 했다. 서로의 테스트가 서로에게 영향을 주지 않도록, 그리고 프로덕트의 코드의 변경이 테스트의 영향을 주지 않도록,또한 테스트하려고 하는 When 외에 변경을 반영하지 않도록 말이다. 🤔 내 생각을 정리하자면테스트 코드는 분명 중요하다.그러나 테스트 코드는 테스트 코드여야 한다.그 책임을 넘어 프로덕트의 책임까지 침범하거나 그 이상의 권한이 주어져서는 안된다고 생각한다. 이번에 적은 양의 코드지만 테스트를 짜면서 느낀 것은 이 적은 양도 꽤 많은 테스트가 필요한데 프로덕트 코드의 테스트는 얼마나 복잡할까?그리고 그걸 지속가능하게 관리하려면 어떻게 해야할까? 등에 대한 생각을 하게 됐다. 다음 주에는 그런 것을 바탕으로 테스트 코드를 관리하는 방법들이나테스트 코드를 분리하는 방법에 대해 같이 공부하면서 강의를 마무리 해야겠다.

백엔드테스트

김진수

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

인프런 워밍업 클럽 2기 발자국 2주차1. 한 주의 정리나머지 뒤의 섹션을 들었고, 직접 리팩토링을 해보니 역시 의도적으로연습하지 않으면 익숙해지기 힘들겠다는 생각이 들었다. ✅ 섹션 6. 코드 다듬기섹션 6의 코드 다듬기는 발견하지 못한 버그를 잡는 다거나 알고리즘의 교체패키지를 정리하고, 변수의 파라미터의 순서 등을 정리하는 등 다양한 리팩토링이 진행된다.알고리즘에 대해서,나는 좀 긍정적인 시각이다.알고리즘에 대해 굉장히 중요하다고 생각하고 활용을 잘 해야한다고 생각하는데,그렇지 않은 입장도 있다. ✅ 섹션 7. 리팩토링 연습본격적으로 코드가 하나 주어지고 해당 코드를 리팩토링 하는 연습을 진행했다.워밍업 클럽의 미션으로도 진행한 해당 리팩토링은 아래의 미션에서 정리하도록 하겠다. 여기서는 확실히 개개인의 관점에 따라 같은 코드가 각자 다르게 리팩토링 되는 걸 경험 할 수 있었다.그 관점이 모두 납득이 가는 충분한 이유였고, 그렇기에 리팩토링과 클린 코드에 대한 이 스터디의 소중함을 느꼈다.2. 미션이번 주차는 직접 리팩토링을 하는 미션을 진행했다.꽤 긴데 가볍게 정리를 해보도록 하자. ✅ 리팩토링나는 크게는 내부와 외부,그리고 객체의 책임과 협력 관점에서 리팩토링을 진행했다. 전체 코드는 Github를 참고하는 게 낫겠다.https://github.com/ckaanf/readable-code/tree/mission/day-7 InputHandler/OutHandler를 바라보는 관점나는 여기서 두 클래스가 행위만 하도록 변경했다.저 클래스에 Message를 던지고 두 객체는 입력을 받거나 출력을 하거나 하는 행위만을 실행한다.그렇기에 입력을 받는 것은 입력을 받아야할 객체,출력을 하는 것은 출력을 해야할 객체로 옮겼는데. 생각해보니 그렇게 하니 입출력의 변경에 따라 객체에 변경사항이 생긴다는 관점이 있었고,"입/출력 양식" 이것이 과연 객체가 가지고 있어야 할 책임인가? 에 대해 다시 생각해보게 되었다. 중복 제거80%가 똑같고 20%만 다른 로직들에 대한 중복제거즉 거의 대부분은 비슷하나 살짝의 분기문이나 팩터의 추가로 달라지는 로직에 대해서 어떻게 처리해야하나 고민이 있었다.이 부분을 Optional로 강의에서는 해결하는 데, 나는 그 부분까지는 고려하지 못했으나 비슷하게는 처리했다.그러나 null 객체에 대한 안정성이 좀 떨어지는 것 같아 강의를 보고 다른 관점에 대해 알게 됐다.  🤔 내 생각을 정리하자면확실히 이론적인 것을 실제로 적용하려하니 아직은 어색하고 머리가 굳는 듯한 느낌을 받았다.역시 의도적으로 자주 생각하고 적용해보는 것이 중요할 것 같다. 여담으로 중간 점검에서 다양한 질문들과 다른 분들에 대한 코드 리뷰까지 정성스레 해주시는 것을 보고정말 괜찮은 강의를 골랐고, 인프런 워밍업 클럽이 좋은 기회였다는 것을 다시 한번 느끼게 됐다.

백엔드

Yoo Seung Hwan

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

I. 2주차 학습 내용 1일차 - 데이터를 다루는 리포지토리 개발하기1) 데이터베이스 초기화테스트 데이터를 초기화 했음 @Component - 스프링에 빈으로 만들 클래스를 알려주는 것설명을 들으때마다 이런 기술들을 통해 스프링의 위대함을 잘 알려주는 것 같다. 스프링이 없었다면 개발을 위해 직접 인스턴스를 생성하고 관리해야 했을텐데 복잡한 서비스에서는 스프링이 필수라는 것을 잘 느낄 수 있는 것 같다. @Profile - 특정 value일 때만 빈으로 생성@PostConstruct - 빈 생성 직후 postConstruc 과정시에 실행 되도록 해줌 1일차 과정 중에는 프로젝트에 사용할 테스트 데이터들을 정의 하고 필요한 곳에 초기화 하였음코틀린 문법에 익숙하지 않아 혼자서 생각하면서 작성하지는 못했으나 파이썬이나 JS, Java 등 프로그래밍 언어에 어느 정도 익숙한 사람이라면 추측 가능한 코드들이 많아서 어렵지 않았습니다. 코틀린 문법에 익숙해지기 위해 코틀린의 리스트 생성등 기초 문법을 찾아 보는 등 다른 공부에 시간을 많이 할애 할 수 있는 시간이였습니다. 2일차 - 리포지토리 테스트 하고 성능 개선하기들어보기만 했던 대망의 테스트 코드 작성@DataJpaTest - Jpa 관련 테스트 어노테이션@TestInstance - 테스트 인스턴스를 관리 강의에서는 인스턴스의 라이프 사이클을 클래스 단위로 정의해주는 역할을 했음assert - 개발 및 디버깅을 전제로 사용하는것, 비즈니스 로직과 검증 로직은 구분할 수 있음 코드나 개념적으로 어려운 부분은 없었음. 테스트 코드를 작성하면서 해야할 생각에 대해 배운게 더 많았다. 데이터가 어떻게 서로 전달되고 결과는 어떻게 나와야할지 클래스를 추상화 하듯이 생각하면서 오류를 잘 발견할 수 있게끔 그러면서도 테스트 코드의 가독성은 높게 작성하는 것이 좋은 테스트 코드라는 생각이 들었다.  개발자 관련된 소식을 접하다 보면 테스트 코드에 대해서 많이 들어보게 됩니다. 저는 아직 제가 테스트 코드 같이 딥한 내용을 배우기보다는 기초에 더 투자할 때라 생각하여 한번도 테스트 코드 작성과 관련해서 고민 해본적도 작성해본적도 없었습니다. 하지만 테스트 코드에 대해서 배우고 나니 생각보다 어려운 개념이 아니였고 기초 단계의 공부와 코드 작성에 많은 도움을 주었습니다. 오류의 탐색 범위를 줄이는데 도움을 주었고. 완성될 프로젝트의 작동 과정에 대해 코드적으로 고민해 보니 다른 코드들이 어떻게 작성되어야 할지 간접적으로 생각해보게 되었습니다. 이는 나중에 제가 개인 프로젝트를 할때 큰 도움이 될것 같았습니다. 항상 어려운 개념이라고 느껴졌던 테스트 코드에 대해서 선입견을 깨는 좋은 경험이였습니다.  3일차 - 데이터를 조회하고 변환하는 서비스 개발 II. 2주차 미션 - REST API 설계하기왜 백엔드가 상상력이 풍부한 사람들이 해야 하는가에 대해 깨달았던 시간 어떤 유튜브 동영상에서 백엔드 개발자는 상상력이 많은 사람들이 해야하는가에 대해 관한 이야기를 하는걸 봤었다.백엔드 개발자는 상상력이 풍부한 사람이여야 한다.당시에는 이해하지 못했지만 미션을 해결하면서 어느 정도 깨달음을 얻은 것 같다. 이번 미션은 REST API 설계였다. 대학교에서 과제를 하면서 카카오 REST API를 활용한적 있는 나한테는 그리 어려운 과제가 아닐거라고 생각했었다. 하지만 API를 사용하는것과 설계하는 것은 달랐다. API를 설계하면서 나는 내가 작성했던 ERD를 다시 보았다. 그때 당시에는 꽤 괜찮은 설계라고 생각했었는데 api를 통해 데이터가 오가는걸 생각하니 잘못된 부분이 보여 몇 군데를 수정했다. 연결관계에는 문제가 없었지만 데이터를 주고 받는데 불필요한 외래키와 중복키가 있어서 제거 하였다. 이번 과제를 하면서 가장 고민했던 부분은 API 설계 툴이였다. 그냥 표로 간단하게 정리해서 설계를 하려 했었는데 다른 분들이 올리신 API 설계를 보니 특정 툴을 이용해서 올린게 너무 멋있어 보였다. 그래서 PostMan, Swagger, GitBook 등을 찾게 되었다. 하지만 안타깝게도 개인적인 사정으로 바쁜 나머지 툴들을 이용해서 과제를 올리지는 못했다. PostMan과 GitBook이 내 마음에 들어서 추후 워밍업 클럽 과정이 끝나면 해당 툴들에대해 공부하는 시간을 가져야겠다.   III. 2주차 회고2주차는 개인적으로 바쁜 시간이였습니다. 개인적인 일들로 많이 바쁜 시간이었고 집에오면 몸이 지쳐서 강의를 제때도 못듣고 대충 듣고 넘어가기도 했던 것 같습니다. 평소라면 그냥 넘어갔다가 기억을 다 잊은 상태에서 다시 강의를 들으러 왔었겠지만 워밍업 클럽을 신청하게 된 다짐과 동기, 그리고 앞으로 해야 할 과제들을 생각하면서 시간 날 때마다 강의를 다시 들어 빠르게 내용을 습득하고 제 것으로 만들 수 있었던 것 같습니다. 시작한지 얼마 안 지난것 같은데 벌써 10월의 절반이 갔고 워밍업 클럽의 진도도 50%가 넘어가고 있는데 저 같이 꾸준하지가 못한 사람들은 마음을 다시 다잡을 때인것 같습니다. 앞으로도 계속 정진하여 완주 하는 것을 목표로 달려보겠습니다.

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

도호

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

시작하며벌써 워밍업 클럽을 시작한지 3주가 되었다. 시간이 정말 빠른 것 같다.진도 따라가랴, 과제 제출하랴 정신없게 보내다보니 하나도 밀리지 않은 나를 발견했을 때 꽤 뿌듯했다.전체 회고는 다음 발자국에서 진행하고 이번 발자국에서는 테스트 코드에 관해서 회고해보고자 한다.3주차 회고 (KPT)Keep계획표에 맞춰서 진도 밀리지 않고 잘 수강했다.과제를 밀리지 않고 제출했다.ProblemDAY 12 과제에서 run() 메서드 테스트 코드가 아쉬웠다. 어떤 방식이 맞을지 잘 모르겠다. 기존 코드를 더 잘게 쪼개야할 것 같다는 생각이 들었다.Try강의를 다 듣고 다시한번 DAY 12 과제 테스트 코드를 짜고 리팩토링을 해보자. 정리이번 강의를 들으면서 Layer 별 테스트 코드 작성에 대해서 생각해보게 되었다. 평소 Persistence layer는 테스트를 잘 하지 않았다. 왜냐면 테스트 코드를 작성하더라도 해피케이스만 작성할 것이기 때문에 의미가 있을까? 라는 생각이 들었다.영속성 레이어의 테스트 코드를 "의미 없는 테스트 코드"라고 생각했던 것인데, 실무를 하면서 영속성 테스트 코드가 도움이 되었던 적이 있었나 고민을 해봤다.Spring Boot 버전을 2.7에서 3.2로 올릴 때 hibernate 버전이 업그레이드 되면서 런타임 에러가 발생한 적이 있었다. 이를 생각하면 또 좋은 것 같기도 하다 ㅎ..

ppusda

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

⭐ 1주 동안 배운 내용을 정리하고 회고하는 시간을 가져보자. 11 ~ 12 일차 - Controller, Test, ViewTest이전과 이어서 Controller를 개발하고 이에 대한 Test를 작성하게 되었다.아래는 작성한 테스트 코드의 일부분이다.@DisplayName("[API 컨트롤러 테스트]") @SpringBootTest // Spring boot를 실제로 띄운 다음에 테스트가 진행 됨 @AutoConfigureMockMvc // MockMvc 관련 설정 세팅 class PresentationApiControllerTest( @Autowired private val mockMvc: MockMvc ) { // 생략 private fun performGet(uri: String): MvcResult { return mockMvc .perform(MockMvcRequestBuilders.get(uri)) .andDo(MockMvcResultHandlers.print()) .andReturn() } } @SpringBootTestSpring Boot 애플리케이션을 테스트 할 때 사용되는 어노테이션이다.실제 애플리케이션 환경과 유사한 환경을 구성하여 테스트를 진행할 수 있다.@AutoConfigureMockMvcSpringMVC를 모의로 테스트 할 때 사용되는 어노테이션이다.MockMvc 객체가 자동으로 구성하고 테스트 환경을 유사하게 구성할 수 있다.MockMvc는 실제 서버를 실행하지 않고도 컨트롤러의 요청 및 응답을 테스트 할 수 있도록 한다.여기서 SpringBootTest와 AutoConfigureMockMvc를 같이 사용하는 것에 모순을 느끼게 되었고, 좀 더 내용을 찾아보게 되었다.@WebMvcTestMVC 레이어를 테스트하는데 사용되는 어노테이션이다.특정 컨트롤러와 그에 관련된 컴포넌트들만 주입받아 특정 컨트롤러의 동작을 집중적으로 테스트 할 수 있다.MockMvc를 자동으로 설정하고 주입하여 요청 및 응답을 테스트 할 수 있도록 한다.기본적으로 Service, Repository를 제외하기에 테스트가 가볍고 빠르게 진행될 수 있다. ⇒ 필요하다면 Mock 객체를 통해 주입할 수 있다.결론적으로 말하자면 아래와 같다.💡 Contorller를 집중적으로 테스트하기 위해서는 @SpringBootTest, @AutoConfigureMockMvc 를 사용하는 것 보다 @WebMvcTest를 사용하는 것이 Controller 단위 테스트 패턴에 적합하다. 그렇다고 @SpringBootTest를 사용하면 안된다는 것은 아니다.실제로 통합 테스트를 작성할 때 (=Controller, Service, Repository 등 여러 컴포넌트가 함께 작동할 때)는 SpringBootTest를 사용하여 실제로 적용될 설정이나 복잡한 시나리오가 정상적으로 작동되는지 확인할 때 사용할 수 있다. ViewThymeleaf를 활용해서 포트폴리오 페이지를 만들어 보게되었다.강사님 처럼 고양이 사진을 넣어보고 싶었지만, 어울리는 사진을 넣기가 힘들기에 가지고 있던 다른 사진으로 대체해보았다.기존 코드에서 중복되는 부분을 fragment와 layout으로 분리하였고, 이를 이용하여 더 깔끔한 코드를 만들 수 있었다. 13 ~ 15 일차 - Admin Controller, ServiceKotlin13 ~ 15일차에 접어들면서 대부분의 Kotlin 문법에 대해서는 꽤 익숙해진 것 같다.3 ~ 5일차에는 변수 선언과 함수 선언 그리고 문자열 작성에 대한 차이점을 다뤄봤었다.이번에는 강의에서 만나봤던 차이점 중 간편하다고 느꼈던 차이점에 대해서 정리해보고자 한다. 먼저, for 문이 간편하게 바뀌었다는 생각을 많이 하게 되었다.변수 선언같은 부분도 사라지고, .. 을 이용해 1에서 10까지의 범위를 지정할 수 있었다.물론, until과 같이 이전 값 까지 증가와 같은 기능과 step()을 이용하여 증가 값을 설정할 수도 있다.for (i in 1..10) // Kotlin for (int i = 1; i <= 10; i++) // Java  두번째로 @RequiredArgsConstructor를 사용하지 않아도 되는 부분이 매우 편했다.Kotlin은 class 생성 시 기본 생성자를 매우 쉽게 작성할 수 있고, 이를 프로퍼티로 생성 해준다. 여기서의 프로퍼티는 자바에서의 필드 뿐만 아니라 getter, setter를 포함한다고 생각하면 된다.이에 대한 자세한 내용은 아래 블로그를 참고하면 도움이 될 것이라고 생각한다.[Java/Kotlin] 필드(Field)와 프로퍼티(Property)는 무슨 차이가 있을까? 이때, val로 매개변수를 선언하게되면 Java 로 생각하자면 불변 필드와 해당 필드에 대한 생성자까지 만들어지게 된다.@Service class AdminAchievementService( private val achievementRepository: AchievementRepository ) @Service public class AdminAchievementService { private final AchievementRepository achievementRepository; public AdminAchievementService(AchievementRepository achievementRepository) { this.achievementRepository = achievementRepository; } // 생략 } 그렇기에 Java에서 생성자 주입을 통해 의존성을 주입 할 수 있게 되며 별도의 @RequiredArgsConstructor 어노테이션을 사용하지 않아도 된다. 마지막으로 그외에도 다양한 편의 기능들이다.val pageAttributes = mutableMapOf<String, Any>( Pair("menuName", "Resume"), Pair("pageName", table.name), Pair("editable", true), Pair("deletable", false), Pair("hasDetails", false) ) // Kotlin에서 Map에 데이터를 넣어줄 때 Pair를 사용할 수 있음 val id = line.slice(0..endIndex).toLong() // Long 타입으로 변환 Pair와 같이 Map에 데이터를 더 편하게 넣을 수 있도록 Key-Value 형식으로 값을 설정할 수 있었고,String의 경우도 단순히 toLong()과 같은 기능으로 변환을 쉽게 할 수 있었다. 이런 것들이 별 것 아닌 거 같아보여도 실제로 쌓이다 보면 꽤나 피로한 요소들이어서 정말 좋다고 느꼈던 것 같다.아직 모르는 부분도 분명히 많을 것이지만 Kotlin을 통해서 만족감을 느낄 수 있었다. 퍼사드 패턴이 과연 유리한가?다음으로 정리할 내용은 퍼사드 패턴에 대해서다.퍼사드 패턴(Facade Pattern)은 구조 패턴의 한 종류로 복잡한 서브 클래스들의 공통적인 기능을 정의하는 상위 수준의 인터페이스를 제공하는 패턴이다. 이는 사실 8 ~ 10일차에서 Service, Repository 부분에서 소개했던 한 번에 의존관계를 주입받을 수 있는 Repository 를 생성했다는 내용에서 사용 된 패턴이다.사용할 때는 사실 모르고 사용했지만, Admin 부분에 대한 강의에서는 퍼사드 패턴을 사용하지 않고 필요한 컴포넌트들만 주입받아 사용하며 강사님께서 “퍼사드 패턴과 이런 방식 중에 뭐가 더 유리한지 생각해보면 좋을 것 같다”고 말씀해주셨다. 기존에 코드를 작성할 때는 디자인 패턴에 대해서 모르기도 했었지만 항상 필요한 것만 불러와서 써도 괜찮다고 생각하고 있었다.하지만, 실제로 퍼사드 패턴을 사용하면서 느낀 점은 “컴포넌트를 묶어 사용할 수 있다면 코드가 많이 줄겠다” 였다.실제로 Presentation 부분에서는 컴포넌트를 묶음으로써 생성자 부분에 작성하게 된 코드를 줄일 수 있었다. 하지만, 반대로 생각해보면 묶을 필요가 없다면 굳이 사용할 필요가 없는 패턴이기도 하다.실제로 Admin 부분을 개발하면서는 각 도메인 부분으로 폴더를 나눠 서비스 로직을 작성했으며, 각 서비스 단에서 필요한 리포지토리만 불러와 사용하였다. 결론적으로 상황에 따라 유리할 수도 있고, 아닐 수도 있다.학습하다 보면 항상 결론은 위와 같이 나오는 것 같은데 그만큼 상황에 맞는 구조와 코드를 잘 적용시키는게 중요하다는 생각을 하게되었다. 서브 미션이번 3주차에는 미션 4를 수행하게 되었다.미션 4는 조회 API를 만들고 이에 대한 테스트 코드를 작성하는 것이 내용이었고 열심히 작성해서 제출하였다.이후 미션 내용을 README에도 정리해보았다.https://github.com/ppusda/MML서브 미션을 진행하면서도 약간의 배운 점이 생겨서 이 또한 정리해보고자 한다. TargetEntity와 MappedBymappedBy의 경우는 많이 사용해봤지만, TargetEntity의 경우는 사용한 적이 없어 어떤 차이가 있는지 궁금하게되었다.targetEntity관계의 대상 엔티티를 명시적으로 지정할 때 사용된다. (관계 대상을 명시적으로 지정)주로 @OneToMany, @ManyToMany와 같은 어노테이션에서 사용하며, 관계의 상대 엔티티 클래스의 이름을 지정한다.mappedBy관계의 주체와 종속성을 정의합니다. (관계의 주체를 설정함)주로 양방향 관계에서 사용되며, 관계의 주체가 되는 쪽에서 어떤 필드가 관계를 관리하는지를 지정한다. 총 회고3주차에는 드디어 화면을 직접 개발해보면서 좀 더 포트폴리오에 다가가고 있다는 생각이 들었다.미션을 따라서 진행하면서 좀 더 나만의 포트폴리오 처럼 꾸밀 수 있는 방법은 뭐가 있을까에 대해서도 생각해보려고 한다. 개발적으로도 성장하고 있는 것이 느껴지기도 하지만 그와 동시에 많이 멀었다는 생각도 계속하게 된다.특히 강사님의 코드를 보며 학습하다보니까 구조나 코드가 정말 정갈하다는 생각을 하게되었다.위에서 정리했던 Kotlin의 특성이나 디자인 패턴을 정말 잘 적용한다는 생각이 들었고, 앞으로 나도 그렇게 할 수 있는 개발자가 되고 싶다는 생각을 하게 되었다. Kotlin에 대해서도 많이 익숙해진 것 같다.Kotlin이 Java의 상위호환이라는 말을 많이 들었는데, 이전 경험이 있어서 크게 기대는 하지 않고 학습을 진행했다.하지만 정말 편한 점이 많이 있었고, 더 공부해서 코루틴 같은 개념도 적용해보고 싶다고 생각했다. 앞으로도 계속 개발자로 공부할 수 있도록 노력해야겠다.모두 화이팅! (o゚v゚)ノ

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

Groot

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

강의 수강수강한 강의s: Readable Code: 읽기 좋은 코드를 작성하는 사고법, Practical Testing: 실용적인 테스트 가이드학습 내용 요약이번주는 클린코드를 완료하고 테스트에 관한 강의로 넘어갔다.Test 필요성, TDD, BDD 등을 배우고 실제 클린코드에서 리팩터링한 코드를 짜보는 시간을 가졌다.회고개인적인 사정(코테, 면접, 스터디, ...)으로 미션 7 이후로 진도 조절에 실패해서 강의자료를 보면서 이해 안 되는 것 위주로 빠르게 수강하고 있다. 뭔가 정신 없이 밤이나 새벽에 하다보니 되게 힘들었다. 오늘 몰아서 진도를 정상화시키고 월요일 과제는 좀 더 여유롭게 할 수 있도록 해야겠다...미션해결 과정스터디카페를 잡고 테스트를 짜보았다. 대충 개념이나 어떻게 짜면 되는지는 이해가 됐는데 막상 시작하려니깐 되게 막막했다. 일단 특수한 모델 객체부터 검증해나가며 테스트를 점진적으로 추가해가다 보니 풀리기 시작했다.하다가 내가 원한 모든 테스트케이스를 완성하지는 못했다. 외부에 의존하는 사이드 이펙트를 다루는게 좀 어려웠다. Scanner나 이상한 데이터를 주입했을 때도 테스트하고 싶었는데 실패했다.회고Scanner 같은 부분을 테스트하려고 하는데 stdin까지 원하는대로 컨트롤하려니깐 이상했다. 외부에 의존하는 부분을 만들려다 그 코드가 엄청 길어지니깐 굳이 Class를 직접 갖다 않고 Mock으로 원하는 방향으로 만들어 주는게 자연스러워보이기 시작했다.한번 Application, PassMachine 코드를 Mock으로 외부 디펜던시를 만들고 검증해보려했는데 한참 삽질하다가 실패했다. 마침 아직 못 본 섹션 7의 Mock이다. 얼른 섹션 7을 수강하고 내가 생각하는 방향이 맞는 점검해보고 다시 시도해볼 예정이다!

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

ronnie

백엔드 클린코드, 테스트코드 회고 3주차

해당 회고는 'Practical Testing: 실용적인 테스트 가이드' 강의에 대한 회고입니다.시작이번주부터는 본격적으로 테스트 코드에 대한 강의를 듣게 되었습니다. 테스트 코드에 대해서 적용은 하고 싶지만 접근 방법에 대해서 몰랐던 저에게 한줄기 희망이 된 강의입니다. 배운 것들3주차에서는 테스트가 왜 필요한지, 사용한다면 어떤 이점이 있는지에 대한 학습을 위주로 했습니다. 테스트 필요성테스트 코드가 없는 경우production code가 커짐에 따라 커버할 수 없는 영역 발생변화가 생기는 순간마다 발생가능한 케이스를 모두 고려해야 한다그에 따른 피드백은 느려지고 이는 유지보수의 어려움으로 이어진다, 소프트웨어의 안정성을 보장할 수 없다 따라서 테스트코드를 작성하는 것이 좋다테스트 코드를 작성할 경우개발 코드에 대한 빠른 피드백을 얻을 수 있으며 자동화를 통해 시스템이 코드를 검증할 수 있도록 할 수 있다이를 통해 서비스의 안정성을 보장할 수 있다 이런 이유에서 테스트 코드를 잘 작성하는 것은 매우 중요하다무엇을 검증하는지 모호하고 혹은 너무 복잡한 테스트 코드라면 무용지물이 될 수 있다. 혹은 잘못된 검증이 이루어질 수 있다 좋은 테스트 코드는 다음과 같다테스트 코드가 자동화되어 빠른 시간에 버그를 발견할 수 있어 사람이 수동 테스트하는 비용을 줄여준다내가 남긴 코드와 테스트에 대해 다른 팀원에게도 공유되어 팀 전체의 공유 자산으로 활용된다짤 때는 귀찮은데 사실 이것이 가장 빠르고 확실하고 좋은 길이다 문서로써 테스트프로덕션 기능을 설명다양한 테스트 코드를 통해 프로덕션 코드를 이해하는 시각과 관점 보완과거에 경험했던 고민의 결과물을 팀 차원으로 승격시켜서 모두의 자산으로 공유 가능! @Test void add() { CafeKiosk cafeKiosk = new CafeKiosk(); cafeKiosk.add(new Americano()); System.out.println("담긴 음료 수 : " + cafeKiosk.getBeverages().size()); System.out.println("음료 상세 : " + cafeKiosk.getBeverages().get(0).getName() + ": " + cafeKiosk.getBeverages().get(0).getPrice()); } 이런 테스트 코드의 경우 문제가 있다어쨋든 마지막에 사람이 직접 확인해야한다다른 사람이 봤을 때 어떤 것이 맞는 테스트인지 틀린 테스트인지 알기 힘들다심지어 이 코드는 틀릴 일이 없다… 단순한 콘솔 찍기이므로..이런 수동 테스트는 효율적이지도 못하고 테스트의 역할조차 제대로 하지 못한다자동화된 테스트로 코드 검증을 자동으로 해주어 개발자의 코드검증 개입을 최소화 해보자  단위테스트단위테스트란클래스나 메서드와 같이 작은 단위를 독립적으로 테스트테스트 속도가 빠르고 안정적테스트를 위한 프레임워크와 라이브러리 이용  테스트 세분화하기암묵적인 요구사항이 있는지 먼저 확인최대한 많은 상황을 커버하기 위해 테스트 세분화는 중요 세분화해피 케이스 → 생각한대로 잘 작동하는지 (ex. 아메리카노를 2잔 주문했다면 주문내역에 2잔이 들어갔는지)예외 케이스 → 작동하지 않는 경우 (ex. 아메리카노 105잔 주문은 불가능)경계 테스트 → 범위, 구간, 날짜에 대해서 정상동작 범위를 명확히 하면 해피 케이스와 예외 케이스의 테스트 생성을 올바르게 할 수 있다.(ex. 3이상까지 정상 동작하는 코드라면 해피케이스로 3, 예외 케이스로 1을 두어 테스트)경계값에서 테스트하는 것이 중요하다경계값에서 설정한 예외와 설정한 메시지가 잘 던져지는지 확인하자 테스트하기 어려운 영역을 구분하고 분리하자테스트 가능한 코드에 테스트가 불가능한 부분이 들어온다면 전체 코드가 테스트 불가능하게 된다. 따라서 테스트하고자 하는 영역을 잘 생각해야 한다. 테스트 영역을 어디까지 분리하는 것이 좋을까?기본적으로는 모호하고 어려운 부분을 외부로 분리할 수록 테스트를 할 수 있는 범위는 늘어난다. 하지만 당연히 적당한 멈추는 선이 있을 것이다.정확하게 어려운 영역이 무엇인지 체크해보고 분리범위에 대해서 알아보자. 테스트 어려운 영역테스트마다 다른 값에 의존하는 코드날짜, 시간, 랜덤 값, 전역 변수나 함수, 사용자 입력외부 세계에 영향을 주는 코드표준 출력, 메시지 발송, DB기록 등 테스트 쉬운 영역 (순수 함수)같은 입력에 항상 같은 결과외부 세상과 단절된 형태테스트하기 쉬운 코드  TDDTest Driven Development테스트 코드를 먼저 작성하고 이후에 프로덕션 코드를 작성해서 테스트 코드를 중심으로 서비스가 구현되는 방법론cf. 애자일 소프트웨어 개발 선언에서 켄트 백이 소프트웨어 개발 방법으로 XP(extream programming)을 제안했고 이것을 실천할 수 있는 방법 중 하나가 TDD TDD과정실패할 수밖에 없는 테스트 작성 (RED)테스트를 통과하기 위한 최소한의 코드 작성 (돌아가기만 하면된다. GREEN)테스트 통과를 유지하면서 작성한 코드를 개선해 나감 (REFACTOR) TDD의 핵심내가 작성하는 프로덕션 코드에 대해서 빠르고 자주 피드백을 받을 수 있다결국 테스트를 잘하기 위한 구조를 고민하게 된다. 시간을 분리했던 코드와 같이 시간이 테스트하기 어려운 부분이라는 것을 먼저 캐치하고 미리 외부로 분리하는 구조를 선택! 선 테스트 작성 후 기능 구현테스트 가능한 코드로 구현하면서 복잡도가 낮고 유지보수가 쉬운 구현이 가능해진다발견하기 어려운 엣지 케이스를 놓치지 않게 해준다구현에 대한 빠른 피드백 가능과감한 리팩토링 가능TDD는 구현부가 테스트와 상호작용하며 발전한다TDD는 클라이언트 관점에서 피드백을 준다 DisplayName과 BDD@DisplayNamejunit5에서 추가된 어노테이션해당 테스트가 어떤 역할을 하는지 명시해줄 수 있다 명사의 나열보다 문장으로 쓰자“~테스트” 형식은 지양하자테스트에 대한 결과까지 써주는 것이 좋다 BDDTDD에서 파생함수 단위 테스트가 아닌 시나리오 기반 테스트케이스에 집중해서 테스트한다개발자가 아니어도 이해할 수 있을 정도의 추상화 수준 권장 작성 방법Given 어떤 환경에서When 어떤 행동을 진행했을 때Then 어떤 상태가 일어난다 Persistence Layer 테스트Persistence Layer?Data Access 역할을 한다비즈니스 관련 작업을 하는 로직이 포함되어서는 안된다data에 대한 CRUD에만 집중한 레이어 Repository Layer에 대한 테스트JpaRepository를 상속하는 경우 쿼리 메서드명을 잘 지을 경우 틀릴 일이 없는데 왜 테스트를 진행할까?→ 간단한 쿼리라면 가시적으로 충분히 확인할 수 있지만 where절의 조건 때문에 메서드명이 길어지거나 파라미터를 잘못 작성할 수도 있다. 날 것 그대로 쿼리를 작성하는 JPQL이나 QueryDSL을 사용하는 경우도 있다따라서 현재 내가 작성한 쿼리가 잘 동작하는지 테스트하는 것도 있지만 이후 다른 방법으로 사용될 쿼리를 미리 검증하기 위해서도 필요하다.Repository 테스트의 경우 서버를 띄워서 하지만 DB에 엑세스하는 부분만 보기 때문에 기능 별로 테스트하는 단위 테스트의 느낌이 있다 cf. 테스트가 통과할거라고 예상하고 테스트했지만 에러 발생java.lang.AssertionError: Expected size: 2 but was: 4 in: [sample.cafekiosk.spring.domain.product.Product@7bb996e0, sample.cafekiosk.spring.domain.product.Product@589cc8eb, sample.cafekiosk.spring.domain.product.Product@44233014, sample.cafekiosk.spring.domain.product.Product@6888a33] 아마 설정파일이 디폴트가 local로 되어 있는데 local 설정은 디비 초기화 후 data.sql이 자동실행되어 이미 저장된 데이터들 때문에 에러가 발생하는 것 같다해당 테스트를 실행할 설정 프로파일을 data.sql이 적용되지 않는 다른 프로파일로 지정해주자!테스트 클래스에 @ActiveProfiles(”적용할 설정 프로파일이름”)만 해주면 된다 마무리 회고이것저것 할 것이 많은 한 주였습니다. 잘 마무리하고 마지막주인 다음주까지 열심히 달리고 싶습니다.워밍업 클럽뿐만 아니라 배운 것들을 정리하면서 기록하는 것이 중요하다는 것을 크게 느낍니다. 앞으로도 차곡차곡 쌓아갈 수 있었으면 좋겠습니다. 앞으로도 화이팅!

웹 개발백엔드클린코드테스트코드워밍업클럽

홍정훈

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

출처: Practical Testing: 실용적인 테스트 가이드1. 학습 내용 요약수동으로 테스트할 경우누락되는 케이스 발생 늦은 피드백유지보수 어려움소프트웨어 신뢰도 낮아짐테스트 코드를 작성하지 않는다면변화가 생기는 매순간마다 발생할 수 있는 모든 Case를 고려해야 함변화가 생기는 매순간마다 모든 팀원이 동일한 고민을 해야 함빠르게 변화하는 소프트웨어의 안정성을 보장할 수 없음테스트 코드가 병목이 된다면프로덕션 코드의 안정성을 제공하기 어려움테스트 코드 자체가 유지보수하기 어려운, 새로운 짐이 되어버림잘못된 검증이 이루어질 가능성이 생김올바른 테스트 코드는자동화 테스트로 비교적 빠른 시간 안에 버그 발견 가능, 수동 테스트에 드는 비용 절약소프트웨어의 빠른 변화 지원 단위 테스트작은 코드 단위(클래스 or 메서드)를 독립적으로 검증하는 테스트검증 속도가 빠르고, 안정적 테스트 케이스 세분화해피 케이스예외 케이스경계값 테스트 → 범위(이상, 이하, 초과, 미만), 구간, 날짜 등테스트하기 어려운 영역을 구분하고 분리하기현재 일시와 같이 테스트가 어려운 코드가 들어오는 경우전체가 테스트하기 어려워질 수 있음테스트가 어려운 부분을 외부로 분리 테스트하기 어려운 영역관측할 때마다 다른 값에 의존하는 코드현재 날짜/시간, 랜덤 값, 전역 변수/함수, 사용자 입력 등외부 세계에 영향을 주는 코드표준 출력, 메시지 발송, 데이터베이스에 기록하기 등테스트하기 좋은 함수순수함수 (pure functions)같은 입력에는 항상 같은 결과외부 세상과 단절된 형태테스트하기 쉬운 코드Test Driven Development프로덕션 코드보다 테스트 코드를 먼저 작성하여 테스트가 구현 과정을 주도하도록 하는 방법론TDD의 3단계Red실패하는 테스트 작성Green테스트 통과최소한의 코딩 Refactor구현코드 개선테스트 통과 유지선 테스트 작성, 후 기능 구현복잡도가 낮은, 테스트 가능한 코드로 구현할 수 있게 해줌쉽게 발견하기 어려운 엣지 케이스를 놓지지 않게 해줌구현에 대한 빠른 피드백 가능과감한 리팩토링 가능 BDD 스타일로 작성Behavior Driven DevelopmentTDD에서 파생된 개발 방법함수 단위의 테스트에 집중하기보다, 시나리오에 기반한 테스트케이스(TC) 자체에 집중하여 테스트 BDD ToolGiven시나리오 진행에 필요한 모든 준비 과정 (객체, 값, 상태 등)When시나리오 행동 진행Then시나리오 진행에 대한 결과 명시, 검증통합 테스트여러 모듈이 협력하는 기능을 통합적으로 검증일반적으로 작은 범위의 단위테스트만으로는 기능 전체의 신뢰성 보장 불가풍부한 단위 테스트 & 큰 기능 단위를 검증하는 통합 테스트2. 미션 회고Day 12. 주어진 코드에 대하여 단위 코드를 작성해보는 미션이었다. 직접 테스트 코드를 작성해보는 건 이번이 처음이었다. 이번 미션에서 어려웠던 부분은 테스트하기 어려운 부분을 통제하는 것이었다. 예를 들어 static으로 클래스 내부에 선언된 Scanner의 경우 입력값을 두 번 이상 넣기가 어려웠다. Scanner를 변수로 빼는 부분도 고려해볼 수 있었지만, 원래 코드의 의도를 테스트때문에 해치는 것 같아 망설이게 되었다. 다행히 강의 뒷편에서 이러한 고민을 다루는 부분이 있는 것 같아 학습 후에 이번 미션에서 고민했던 것들을 확인해 볼 수 있을 것 같다. 3. KPTKeep미션을 해결하고 다른 사람들의 코드들을 보면서 놓쳤던 케이스들을 확인하였다.Problem테스트 환경을 제어하는 부분들 중 어려운 것이 많았다.Try 강의 후반부에서 지금까지 생긴 궁금증들을 많이 다루고 있어서, 나중에도 기억할 수 있도록 정리하면서 들어야겠다.4. 느낀점3주차 테스트 코트 강의가 시작되었다. 워밍업 클럽에 참가한 이유 중에 하나이기도 하다. 이전에 서비스 코드는 작성해 본 적이 있지만 단위 테스트, 통합 테스트는 작성해 본 적이 없었다. 테스트를 작성하지 않았던 이유는 강의 초반에서 강사님이 언급했던 것처럼 '귀찮음'이 가장 컸었다. 그러나 여러 테스트 코드를 작성하고, 전체 테스트를 돌려 모두 성공했을 때의 기쁨은 그 귀찮음을 충분히 보상해 주는 것 같다. 또한, TDD 방식으로 구현을 연습해보았는데, 테스트를 먼저 작성하고 코드를 작성하는 방식이 다소 낯설었다. 그러나 기존의 구현 우선 방식보다 확실히 여러 엣지 케이스를 떠올리기가 쉬웠다. 이 방식을 습관화하여 자연스럽게 활용할 수 있도록 노력해야겠다.

프로그래밍 언어

yoon

[워밍업 클럽 2기 BE 클린코드&테스트] 3주차 회고

들어가며테스트 코드에 대해 깊이 있게 학습하면서, 단순히 코드 검증을 넘어서 더 나은 설계와 신뢰성 있는 애플리케이션을 만들기 위한 다양한 인사이트를 얻을 수 있었습니다.주요 학습 내용1. 단위 테스트의 본질 이해단위 테스트를 작성할 때 가장 중요한 것은 테스트 케이스를 세분화하고, 테스트하기 어려운 영역을 명확히 분리하는 것임을 배웠습니다. 이는 단순히 테스트 커버리지를 높이는 것이 아닌, 코드의 품질을 실질적으로 개선하는 방향으로 이어져야 한다는 점을 깨달았습니다.2. TDD의 실천적 의미TDD가 단순한 방법론이 아닌, 설계 품질을 개선하는 도구라는 점을 이해했습니다. Red-Green-Refactor 사이클을 통해 점진적으로 코드를 개선해나가는 과정이 중요하며, 이는 결과물의 품질을 자연스럽게 향상시키는 방법이라는 것을 배웠습니다.3. 테스트 코드 작성의 실용적 접근특히 인상 깊었던 부분은 "테스트는 문서다"라는 관점입니다. DisplayName을 섬세하게 작성하고 BDD 스타일로 테스트를 구성함으로써, 테스트 코드가 단순한 검증을 넘어 살아있는 문서의 역할을 할 수 있다는 점이 새로웠습니다.4. Spring & JPA 환경에서의 테스트각 계층별 테스트 전략이 다르다는 것을 배웠습니다:- Persistence Layer: 실제 DB를 사용한 통합 테스트의 중요성- Business Layer: @Transactional 사용에 대한 신중한 접근- Presentation Layer: MockMvc를 활용한 효과적인 테스트5. Mock 사용에 대한 인사이트Mock을 무분별하게 사용하는 것이 아닌, 필요한 상황에서 적절하게 활용하는 것의 중요성을 배웠습니다. 특히 Classicist와 Mockist의 관점 차이를 이해하면서, 상황에 따른 적절한 선택의 중요성을 깨달았습니다.개선하고 싶은 부분1. 테스트 가독성 향상- BDD 스타일의 일관된 적용- 더 명확한 DisplayName 작성2. 테스트 격리성 개선- 외부 의존성을 최소화하는 설계 고려- 시간이나 외부 호출 같은 제어하기 힘든 값들에 대한 더 나은 처리 방법 연구3. 효율적인 테스트 구조화- 테스트 코드의 중복 최소화- 재사용 가능한 테스트 유틸리티 개발앞으로의 다짐1. 테스트를 통한 문서화를 더욱 강화하여 코드의 의도를 명확히 전달하겠습니다.2. TDD를 실무에 점진적으로 도입하여 코드 품질을 개선하겠습니다.3. Mock 사용에 있어 더 신중한 판단을 하고, 실제 객체를 사용할 수 있는 상황에서는 실제 객체를 사용하도록 하겠습니다.마치며테스트 코드는 단순한 버그 예방 차원을 넘어, 더 나은 설계를 이끌어내는 도구이자 살아있는 문서라는 점을 깊이 이해하게 되었습니다. 앞으로도 이러한 인사이트를 바탕으로 더 나은 테스트 코드를 작성하기 위해 노력하겠습니다.

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

jenhuhh

인프런 워밍업 프로덕트 디자인 2기 3주차 발자국

강의 요약이번 주는 배리어블을 다크모드/멀티 브랜드/반응형 디자인/다중언어 등 모드를 설정해서 적용하는 방법을 배웠고, 이때까지 만들었던 베리어블과 컴포넌트들을 활용하여 페이지에 적용해봤다. 특히, 베리어블을 여기저기 적용해보니 신기함과 놀라움의 연속이었다!저번 주만 해도 베리어블과 이제 많이 친해졌다고 생각했는데, 페이지를 만들면서 다시 사이가 멀어진 것 같은 느낌..... 🥲 너무 늦어지기 전에 다양한 페이지를 만들어보면서 손에 익힐때까지 복습이 필요할 것 같다.다크모드다크모드를 실무에 적용해본적이 한번도 없기 때문에 특히나 집중해서 강의를 들었다. 강의 내용대로 작업하고, 테스트하고, 확인하는 작업이 반복적으로 이루어졌는데, 내가 봤을 때 이뻐보이는(?) 것과 접근성 그 사이 어딘가에서 많이 해맸었던 것 같다.서비스 성향에 따라 추천하는 모드푸드, 이커머스와 같은 컨텐츠가 돋보여야하는 서비스는 라이트 모드를 추천몰입형 미디어 (OTT 서비스)는 다크모드를 추천함결국 브랜드 아이덴티티와 접근성을 고려하고 끊임없는 테스트를 통해 수정이 필요하다.브랜드브랜드 컬러 베리어블을 적용하기 위해 프리미티브에 컬러를 추가가 필요했는데, 1주차 때보다 훨씬 속도가 빨라졌고, 좋은 복습하는 기회가 되었다.브랜드 컬러 모드는 theme 컬렉션에 추가하는데, 'brand'라는 컬러를 semantic에 설정해놨기 때문에, Primitive > theme > semantic 이런식으로 참조가 여러 단계로도 된다는걸 적용해보면서 신기하게 느껴졌었다.반응형 디자인디바이스 크기마다 number로 사이즈만 조절하는 것이 아니라, boolean을 활용해 컴포넌트에 적용할 수 있다는 점이 흥미로웠다.boolean 프로퍼티의 경우 눈 아이콘에서 우측 버튼을 클릭해야 적용 할 수 있는데, 강의 후반부에 이걸 까먹어서 한참을 해맸었다. 😱회고잘한 점이번 주는 회사일이 많이 바빠서 힘들었지만, 포기하지 않고 모든 미션을 마무리했다. 아쉬운 점미션을 하다 막혔을 때 스스로 할 수 있는데까지 해보고 질문 남길 것

UX/UI

Taeho

인프런 워밍업 클럽 - CS Day 14

Algorithm재귀가 성능에 영향을 미치는 경우Call Stack중복되는 연산→ 중복되는 연산을 저장하고, 같은 계산이 필요할 때 저장된 결과를 사용한다.→ 함수 호출 수가 줄어듦.→ 성능이 좋아진다.Memoization계산 결과를 저장해서 여러 번 계산하지 않도록 하는 기법단점속도를 위해서 메모리(저장 공간)를 사용한다.재귀 함수이기 때문에 Call Stack을 차지하는 단점은 변하지 않는다.Tabulation상향식 계산 방식계산에 필요하지 않을 수 있는 값도 미리 계산해 테이블에 저장하는 방식Memoization vs TabulationMemoization : 해결하기 힘든 문제를 하향식으로 접근하여 복잡한 문제를 쉽게 해결할 수 있다.분할 정복을 해결할 때 재귀가 더 직관적인 경우에 유리Tabulation재귀가 직관적이지 않은 경우에 유리OSFile저장장치에 저장된다.구조Header파일의 속성이 담겨있다.Data데이터의 집합데이터 집합 구성에 따른 분류순차파일구조파일의 내용이 연속적으로 이어진 형태ex) 카세트테이프장점모든 데이터가 순차적으로 기록되기 때문에 공간의 낭비가 없고, 구조가 단순하다.단점특정지점에 바로 이동이 어려워 데이터를 삽입하거나 삭제하려면 탐색하는데 시간이 오래 걸린다.직접파일구조저장하려는 데이터의 위치를 해시 함수를 통해 결정하는 구조자료구조에서 Hash Table이라고 불린다.ex) JSON장점해시 함수를 사용하기 때문에 데이터 접근이 굉장히 빠르다.단점해시함수의 선정이 굉장히 중요하다.저장공간이 낭비될 수 있다.인덱스 파일 구조순차접근과 직접접근 방식의 장점을 취한 구조 <탐색키, 레코드 포인터> 쌍으로 구성데이터 파일과는 별도로 저장되며, 빠른 검색을 위해 사용된다.File System파일을 관리하기 위해 OS가 둔 파일 관리자파일 테이블을 이용하여 파일을 관리한다.기능파일과 디렉토리 생성파일과 디렉토리 수정/삭제파일 권한 관리무결성 보장백업과 복구암호화동작블록 저장장치에 저장 → 전송 단위 : 블록사용자가 바이트 단위로 파일에 접근이 가능하도록 지원한다.File Descriptor(= File Control Block)OS가 파일을 제어하기 위한 정보를 보관하는 Block파일마다 독립적으로 존재한다.저장장치에 존재하다가 파일이 오픈되면 메모리로 이동한다.OS가 관리하고 사용자가 직접 참조할 수 없다.사용자는 File Descriptor를 사용하여 File에 접근할 수 있다.파일의 맨 앞에 위치해서 사용자가 쓰거나 읽기를 시작하면 처음부터 진행한다.Directory1개 이상의 파일과 자식 디렉토리를 가질 수 있다.운영체제 마다 가장 최상단의 디렉토리 명칭이 다르다.Windows : Partition Name(ex : C:)Unix, Linux : Root('/')디렉토리도 파일정보가 저장되어 있는 하나의 파일이다.구조디렉토리 헤더 : 디렉토리 정보가 시작하는 위치를 가리킨다.다단계 디렉토리어떠한 디렉토리에서도 하위 디렉토리를 만들 수 있는 트리 구조File & Disk전체 디스크 공간을 블록(일정한 크기를 갖는 공간)으로 나누고, 그 공간에 주소를 할당해 관리한다.- 일반적으로 한 블록의 크기는 1 ~ 8KB이다.- 하나의 파일은 여러개의 블록으로 이루어진다.파일시스템은 파일정보를 파일테이블로 관리한다.파일이 시작하는 블록의 위치정보가 담겨있다.블록 연결 방식에 따른 분류연속할당파일을 구성하는 블록들을 디스크에 연속적으로 저장하는 방식파일의 시작 블록만 알면 파일의 전체를 알 수 있다.외부 단편화가 발생하여 실제로 사용하지 않는다.불연속할당디스크의 비어있는 공간에 데이터를 분산하여 저장하는 방식- 분산된 블록은 파일시스템이 관리한다.연결 할당파일에 속한 데이터를 연결리스트를 사용하여 관리한다.파일테이블에는 시작 블록에 대한 정보만 저장하고 나머지는 연결리스트를 이용해 다른 블록에 접근한다.인덱스 할당테이블의 블록 포인터가데이터 블록에 직접 연결하는 것이 아니라 데이터들의 인덱스를 갖고 있는 인덱스 블록을 연결한다.데이터가 많아서 테이블이 꽉 찬경우 인덱스 블록을 더 만들어 연결하기 때문에 테이블 확장에 용이하다.파일 크기가 작다면 데이터를 바로 참조하는 블록 포인터를 이용한다.파일의 크기가 크다면 간접 포인터를 이용해 많은 데이터에 접근할 수 있다.i-node라는 방식으로 유닉스와 리눅스에서 많이 사용되고 있다.Free Block List파일시스템이 효율적인 공간 관리를 위해 빈 공간을 모아둔 목록파일 시스템은 파일의 모든 정보를 지우는 것이 아니라 파일 테이블의 헤더를 삭제하고 Free Block List에 추가한다.→ 포렌식을 통해 데이터를 복구할 수 있다.

알고리즘 · 자료구조워밍업클럽CS전공지식DAY14

나는뉴비

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

강의Practice Testing: 실용적인 테스트 가이드강의 수강섹션 2. 테스트는 왜 필요할까?테스트를 왜 작성해야하는지 작성하지 않았을 때와 비교해 필요성 체감테스트를 프로덕션 영역과 구분된, 부수적인 대상이 아닌 프로덕션 영역과 같이 유지보수 해야 하는 대상으로의 관점섹션 3. 단위 테스트단위 테스트를 왜 해야하는지, 어떤 식으로 해야하는지 학습 단위 테스트 진행 시 static method와 같은, 테스트하기 어려운 부분을 분리해야 하는 이유 학습 섹션 4. TDD: Test Driven Development프로덕션 코드보다 테스트 코드를 먼저 작성하여 테스트가 구현과정을 주도하도록 하는 방법론인 TDD 학습 Red - Green - Refactor 과정을 통해 객체가 아닌 메시지부터 테스트하는 방식 실습테스트하고자 하는 대상에 집중할 수 있으며, 해피 케이스 뿐만 아닌 에러 케이스도 고려할 수 있게 됨섹션 5. 테스트는 []다테스트는 빠른 피드백과 애플리케이션에 대한 신뢰뿐만 아니라, 문서의 역할도 할 수 있음을 학습문서의 역할을 하기 위해 @DisplayName, BDD 등 가독성을 위한 내용 학습 섹션 6. Spring & JPA 기반 테스트 - Persistence LayerPersistence Layer에서는 어떠한 부분을 고려하며 테스트를 진행해야 하는지 학습하고 실습 JPA라는 ORM을 사용하기 때문에 쿼리를 직접 작성하지 않으므로, 작성한 코드가 의도한 쿼리를 실행하는지 테스트테스트 코드에서 @Transactional을 활용할지 여부에 따라 @DataJpaTest / @SpringBootTest 중 어떤 것을 사용해야하는지 고려미션Day12Readable Code: 읽기 좋은 코드를 작성하는 사고법 studycafe를 대상으로 테스트 코드 작성어떤 대상을 테스트해야하는지 고려해 model 패키지 내에 위치한 클래스에 대해서만 코드 작성Scanner를 static으로 사용하는 InputHandler는 테스트하기 어렵다고 판단OutputHandler의 경우 데이터를 단순 출력하는 용도이므로 테스트를 할 필요가 없다고 판단LockerPassFileReader, SeatPassFileReader의 경우 static으로 조회하려는 파일을 구체적으로 알고 있기 때문에 테스트 시 프로덕션 영역(resource)에 영향을 끼치기도 하고, 테스트하기 어렵다고 판단통합 테스트를 진행해야 하는 StudyCafePassMachine의 경우 InputHandler의 테스트 어려움과 OutputHandler를 통해 검증해야 한다는 점으로 인해 테스트하기 어렵다고 판단회고테스트가 왜 필요한지 다른 사람에게 설득할 수 있을 정도로 학습할 수 있었다미션을 진행하며 아직 테스트를 작성한 경험과 노하우가 부족함을 느꼈다테스트를 할 때 나만의 기준과 그에 대한 근거가 필요함을 느꼈다  

웹 개발워밍업

표수진

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

[3주차 강의 내용 요약]이번주는 마지막 컴포넌트인 네비게이션 컴포넌트를 만들고, 배리어블 모드를 활용하여 기존에 만들었던 컴포넌트에 다크모드를 적용하는 방법과 멀티 브랜드 구현 방법, 반응형 디자인 적용 방법, 다국어 지원 방법에 대해 배워보았다. 또한, 만들어진 컴포넌트와 배리어블 모드를 활용하여 B2B 이커머스 어드민 페이지까지 제작해보는 시간을 가졌다. 1) 네비게이션 컴포넌트(Navigation Component) 만들기텍스트링크(Link), 브레드크럼프(Breadcrumbs), 네비게이션 탭(Tabs), 모바일 하단 네비게이션(Bottom Navigation), 페이지네이션(Pagenation), 사이드 네비게이션(Side Navigation), 헤더(Header), 캐러셀(Carousel) 2) 배리어블 모드 활용 방법[1] 다크모드 전환, 원리 배우기다크모드 : 빛을 덜 발산해서 배터리 절약, 저시력자의 경우 다크모드 선호몰입형 미디어 보기를 지원하는 UI의 경우, 영구적 다크모드 사용(회사에서 다루는 콘텐츠, 상품에 따라 선택!)단순히 라이트 모드 색상 체계를 반전하는 것 XX -> 브랜드 아이덴티티와 접근성을 항상 고려해야 함!!'계획 - 실행 - 테스트 및 무한 수정'의 방식으로 고쳐가는 것이 중요    [2] 브랜드 구조와 모드를 활용한 멀티 브랜드 구현다양한 브랜드 구조가 있고, 유형별로 다른 브랜드 전략을 사용통합 브랜드 형식의 멀티 브랜드 구현을 위한 배리어블 모드 제작동일한 파운데이션으로 브랜드 색상(Theme 색상)만 다르게 제작해보기   [3] 디바이스별 반응형 디자인 구현데스크탑, 태블릿, 모바일 화면에 따라 배리어블 모드 제작Break point, padding, margin 등 다양한 요소 적용 가능헤더나 사이드 네비게이션 등 디바이스에 따라 컴포넌트 타입도 다양하게 변경할 수 있음 !  [4] 다중언어 지원 화면 구현(영어, 한국어, 아랍어 등등)시장 개척 및 점유율 확대, 사용자 적용성 등 글로벌 시장이 목표라면 중요한 부분글로벌라이제이션(웹 접근성 등), 로컬라이제이션(지역, 문화에 따른 다양성) 적용언어별 읽는 방향, 쓰는 방향 변경 가능  우선 배리어블 모드를 활용해서 다크모드, 반응형 디자인 등 웹/앱 디자인에서 중요한 부분을 생각보다 쉽게 해결할 수 있다는 게 놀라웠다. 기존에는 이런 부분을 다 따로 따로 적용할 생각을 했었던 나의 무지함에 지금이라도 이렇게 배워가서 다행이라는 생각도 들었다..😅 그리고 분명 잘 만들었다고 생각했던 컴포넌트들이 모드가 먹히지 않는 부분들이 있어서 로컬 컴포넌트를 찾아가보니 내가 색상을 잘못 설정하는 등등의 실수가 있어서 역시 디자인 시스템을 만들 때는 테스트와 무한 수정의 과정이 꼭! 필요한 부분이구나를 다시 깨닫는 순간이였다..ㅎㅎ 파운데이션부터 시작해서 이제는 내가 만든 컴포넌트로 실제 페이지까지 디자인해보니 신기하면서도 재밌었지만 벌써 강의가 거의 끝나감에 아쉬운 마음이 들기도 했다. 요즘 볼드님의 강의를 들으면서 디자인 시스템에 대한 필요성과 효율성에 대해 크게 느끼고 있어서 계속 공부하고 싶어서 우선은 내가 기존에 작업했던 디자인 작업물을 토대로 디자인 시스템을 제작해보면서 익숙해져봐야겠다!  [이번주 회고]스스로 칭찬하고 싶은 점이번주도 계획에 맞게 강의를 듣고 미션을 수행하였다.기존에 디자인했던 작업물들을 피그마로 수정해보면서 강의에서 배웠던 내용도 함께 적용해보고 있다.auto layout에 대해서 기존엔 헷갈리는 부분이 많았는데 작업을 하면서 많이 익숙해진 것 같다! 아쉬웠던 점, 보완하고 싶은 점이제는 강의를 듣고 미션을 따라해보는 것에서 그치지 않고 내 작업물로 디자인 시스템을 구축해보면서 계속해서 경험을 쌓아야겠다고 생각했다.다크모드에 색상 적용하는 부분에서 생각보다 많은 시간을 보냈던 것 같다. 특히 다크모드 반영시에 접근성의 중요하다는 것을 뼈져리게 느꼈다 ㅠㅠ

UX/UIUX/UI프로덕트디자인디자인시스템피그마Figma

전석희

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

 3주차때 배운 것네비게이션 컴포넌트(링크, 브레드크럼, 탭, 바텀네비게이션, 페이지네이션, 사이드네비게이션, 캐러셀)모드를 활용해보기 (라이트<->다크, 브랜드 컬러 변경)배웠던 것을 활용해 B2B 이커머스 어드민 페이지 만들기벌써 3주차 발자국을 남긴다!! 지금까지 빼먹거나 엄청 미루거나 하지 않았던 나에게 먼저 칭찬 스티커를 주고..작업하고 디스코드에 올리는 과정을 하니까 뭔가 빼먹지 않으려고 하는 것 같았다. 이번에는 네비게이션 컴포넌트를 만들었고, 배리어블에 모드를 추가해 다크모드에 잘 대응하도록 하는 것을 해보았는데 막상 컬러를 설정하니까 컬러가 너무 밝거나 어디는 안맞거나 하는 경우가 있었다. 그래서 그걸 조절하는게 꼭 필요하고.. 하다보니까 내가 컬러부분이 조금 아쉽다는 생각이 들어서 많은 수련(?)이 필요하는 것을 알게되었던 순간... 모드를 통해서 딸깍 바꾸면 슉 바뀌는 것이 편리했다! 역시 유료버전이 좋구나 (지금은 교육계정이지만)반응형이랑 이미지컴포넌트도 배리어블에 설정해서 변경되는 것을 처음 알아서 신기했고 추후에 피그마가 더~ 편리하게 해줄 수 있지만... 지금도 너무 신기했던 경험... 아쉬웠던 점자꾸... constraints 부분을 까먹는다..! 만들고 확인하는 과정을 꼭꼭 거치도록하자다음에 시도할 점나머지시간에는 강의를 무사히 끝내는 것에 초점을 가지고 진행할 예정이다.. 잘 마무리했으면 좋겠다!!

UX/UI피그마배리어블UXUI디자인

gotjd9773

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

개요테스트 코드 학습을 시작했다.Practical Testing : 실용적인 테스트 가이드3주차 공부한 내용수강한 강의 영상은 얼마 안 되는 거 같지만, 러닝 타임이 어마어마하다.확실히 긴 러닝타임의 영상은 힘들다.그래도 공부할만한 내용을 배우고 있다고 되뇌이며 강의를 수강했다. 배운 것들1. TDD 적용하기TDD를 처음 해본 것은 아니다.TDD 주제 책들을 읽은 적도 있고, 직접 해 보기도 했다.TDD를 처음했을 때 깨달은 것은 "단일 클래스의 메소드를 TDD로 개발하는 것은 어렵지 않으나, 여러 계층이 있는 스프링 프로젝트에서 TDD를 적용하는 건 어렵구나. 어떻게 적용하지?" 였다. 강의를 통해서 스프링 프로젝트에서 TDD를 적용하는 방법을 배울 수 있었다. 2. DisplayName을 작성하는 법테스트 메서드의 이름이나, DisplayName을 배웠다.항상 중구난방, 기준도 없이 작성해서 고민이 많았던 내용인데,고민 해결! 3. 재고 차감시 재고가 있는지 두 번 체크하는 이유서비스에서 재고 체크 한 번, 엔티티 객체 안에서 재고 체크 한 번동일한 로직이 두 번 반복해줘야 하는 이유를 배울 수 있었다. 4. 컨트롤러에서 하는 유효성 검증, 서비스에서 하는 유효성 검증유효성 검증을 컨트롤러에서만 하는 게 아니라 계층에 맞는 유효성 검증이 있다는 것을 배웠다.  미션이번 주차에는 Day 12. 미션이 있었다. Day 12. 미션 - 읽기 좋은 코드 강의 예제 코드 단위 테스트 작성하기Readable Code: 읽기 좋은 코드를 작성하는 사고법 강의의 예제 프로젝트로 지뢰찾기와 스터디카페가 있다.둘 중 하나를 선택하여 3개의 클래스, 7개의 단위 테스트를 작성하는 것이었다.나는 스터디카페를 선택하였다.강의에서 배운 내용을 그대로 적용만 하면 되는 미션이라 수월하게 마칠 수 있었다. 마무리다음 주차 부터는 Mocking 에 대해서 배우게 된다.항상 테스트 코드를 작성할 때마다, Mocking 을 사용할 때마다 찝찝함을 느끼곤 했다.이번 기회에 제대로 정리하고 가야겠다.

백엔드읽기좋은코드클린코드워밍업클럽테스트

gusdnchl7144

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

 3주차부터 Practical Testing (실용적인 테스트 가이드) 강의로 넘어가게 되었다.  Section1) Intro강의에서 학습하게 될 것1) 테스트 코드가 필요한 이유2) 좋은 테스트 코드란?3) 실제 실무에서 진행하는 방식 그대로 테스트를 작성해가면서 API를 설계하고 개발하는 방법4) 정답은 없지만, 오답은 있다! 구체적인 이유에 근거한 상세한 테스트 작성 팁Section2) 테스트는 왜 필요할까?1) 테스트는 왜 필요할까?- 테스트 코드를 작성하지 않는다면 코드의 변화가 생기는 매 순간마다 발생할 수 있는 모든 Case를 고려해야 한다..2) 테스트 코드가 병목이 된다면?- 테스트코드 자체가 유지보수하기 어려워진다.- 잘못된 검증이 이루어질 가능성이 생긴다..3) 올바른 테스트 코드는?- 자동화 테스트로 비교적 빠른 시간 안에 버그를 발견할수 있고, 수동 테스트에 드는 비용을 크게 절약할 수 있다. Section3) 단위 테스트Junit5로 테스트하기1) 단위 테스트- 작은 (ex, 클래스 or 메서드) 코드 단위를 독립적으로 검증하는 테스트- 검증속도가 빠르고, 안정적이다.2) Junit5- 단위 테스트를 위한 테스트 프레임워크- XUnit - Kent Back, - Sunit (Samltalk), Junit(Java), NUnit(.NET)- Spring-boot-starter-test 라이브러리를 통해 사용 가능하다..3) AssertJ- 테스트코드 작성을 원활하게 돕는 테스트 라이브러리- 풍부한 API, 메서드 체이닝 지원- Spring-boot-starter-test 라이브러리를 통해 사용 가능하다.테스트 케이스 세분화1) 해피 케이스- 요구사항을 그대로 만족하는 케이스- 경계값 테스트 : 범위 (이상, 이하, 초과, 미만), 구간, 날짜 등이 중요.2) 예외 케이스- 경계값 테스트 : 범위 (이상, 이하, 초과, 미만), 구간, 날짜 등이 중요.테스트하기 어려운 영역 분리하기1) 테스트하기 어려운 영역- 관측할때마다 다른 값에 의존하는 코드→ 현재 날짜/시간, 랜덤 값, 전역변수/함수, 사용자 입력 등- 외부 세계에 영향을 주는 코드→ 표준 출력, 메시지 발송, 데이터베이스에 기록하기 등.2) 순수 함수 - 테스트하기 쉬운 코드- 같은 입력에는 항상 같은 결과- 외부 세상과 단절된 형태- 테스트하기 쉬운 코드 Section4) TDDTDD (Test Driven Development)1) TDD- 프로덕션 코드보다 테스트 코드를 먼저 작성하여 테스트가 구현 과정을 주도하도록 하는 방법론.2) TDD의 핵심 가치- 기존) 선 기능 구현, 후 테스트 작성의 문제점→ 테스트 자체의 누락 가능성→ 특정 테스트(==해피 케이스) 케이스만 검증할 가능성→ 잘못된 구현을 다소 늦게 발견할 가능성- TDD) 선 테스트 작성, 후 기능 구현→ 복잡도가 낮은(==유연하며 유지보수가 쉬운), 테스트 가능한 코드로 구현할 수 있게 한다.→ 쉽게 발견하기 어려운 엣지(Edge) 케이스를 놓치지 않게 해준다.→ 구현에 대한 빠른 피드백을 받을 수 있다.→ 과감한 리팩토링이 가능해진다.Section5) 테스트는 [문서]다테스트는 [문서]다1) 테스트 == 문서- 프로덕션 기능을 설명하는 테스트 코드 문서- 다양한 테스트 케이스를 통해 프로덕션 코드를 이해하는 시각과 관점을 보완- 어느 한 사람이 과거에 경험했던 고민의 결과물을 팀 차원으로 승격시켜서, 모두의 자산으로 공유.DisplayName을 섬세하게1) DisplayNamed을 섬세하게 적기- 메서드 자체의 관점보다 도메인 정책 관점으로, 도메인 용어를 사용하여 한층 추상화된 내용을 담기- 테스트의 현상(ex, 성공한다… 실패한다 등)을 중점으로 기술하지 말 것.BDD 스타일로 작성하기1) BDD (Behavior Driven Development)- TDD에서 파생된 개발 방법으로- 함수 단위의 테스트에 집중하기보다, 시나리오에 기반한 테스트케이스(TC) 자체에 집중하여 테스트하는 기법- Given / When / Then 방식 사용→ Given : 시나리오 진행에 필요한 모든 준비 과정 (객체, 값, 상태 등)→ When : 시나리오 행동 진행→ Then : 시나리오 진행에 대한 결과 명시, 검증.Mission 12) 단위 테스트 작성studycafe 프로젝트에서 InputHandler, StudyCafePassOrder, StudyCafeSeatPass 클래스에 대한 테스트 코드를 작성하였다.고민했던 점으로는 Scanner를 통해 사용자 입력을 받는 로직을 테스트하기 위해 Mock을 사용할 수 있지만, Mock 대신 InputStream과 System.setIn()을 통해 입력해주는 방식을 선택했다.작은 메서드 단위로 단위 테스트를 진행하니 코드도 복잡해지지 않고 간결하여 테스트 하기가 수월하였다.Github Code. Section6) Spring & JPA 기반 테스트레이어드 아키텍처(Layered Architecture)1) Layered Architecture- Presentation Layer, Business Layer, Persistence Layer로 구분- 아키텍처를 분리하는 이유 : 관심사의 분리.Spring / JPA 훑어보기 & 기본 엔티티 설계1) Library vs Framework- Library : 외부에서 이미 개발된 코드를 가져온다. (내 코드가 주체가 되어 동작)- Framework : 이미 동작하는 환경이 구성되어 있고, 내 코드는 프레임안에서 수동적으로 동작.2) Spring- IoC (inversion of Control) : 객체의 생성과 의존성 관리를 프레임워크에서 대신 수행- DI (Dependency Injection) : 외부에서 객체 간의 의존성 주입을 통해 결합도를 낮춰줌- AOP (Aspect Oriented Programming) : 공통 관심사를 별도의 로직으로 분리해 코드 중복을 줄여주고 모듈화.3) JPA- Java Persistance API- Java 진영의 ORM 기술 표준 - 반복적인 CRUD SQL을 생성 및 실행해주고, 여러 부가 기능들을 제공- Spring 진영에서는 JPA를 한번 더 추상화한 Spring Data JPA 제공- 주로 사용되는 어노테이션들→ @Entity, @Id, @Column→ @ManyToOne, @OneToMany, @OneToOne, @ManyToMany .Layer별 테스트1) Persistence Layer- Data Access의 역할- 비즈니스 가공 로직이 포함되어서는 안됨, Data에 대한 CURD에만 집중한 레이어.2) Business Layer- 비즈니스 로직을 구현하는 역할- Persistence Layer와의 상호작용(Data를 읽고 쓰는 행위)를 통해 비즈니스 로직을 전개- 트랜잭션을 보장.3) Presentation Layer- 외부 세계의 요청을 가장 먼저 받는 계층- 파라미터에 대한 최소한의 검증을 수행.회고- 테스트 코드 작성을 의무적으로 작성 하지 않는 환경에서 일을 해왔는데, 스터디를 진행하면서 단위 테스트 코드 작성의 중요성과 필요성을 깨달았다. 모든 예외 케이스를 조기에 발견해서 대처할 순 없겠지만 내가 작성한 코드 범위내에서 테스트가 필요한 영역들을 분리하여 로직이 의도한 대로 동작하는지 검증하는 작업은 백엔드 개발자의 숙명이라는 생각이 들었다.이제 Mock과 테스트 방법론에 대한 강의가 남아있는데, 마지막 주차도 지금처럼 묵묵히 학습해 나갈 것이다.출처https://inf.run/zgJk5https://inf.run/kHiWM 

백엔드워밍업클럽테스트코드

하양이

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

Day 10. 단위 테스트테스트는 왜 필요할까?빠른 피드백자동화안정감단위 테스트작은 코드 단위(클래스 or 메서드)를 독립적으로 검증하는 테스트검증 속도가 빠르고, 안정적이다.테스트 케이스 세분화하기해피 케이스예외 케이스경계값 테스트테스트하기 어려운 영역을 분리하기테스트하기 어려운 영역관측할 때마다 다른 값에 의존하는 코드외부 세계에 영향을 주는 코드순수 함수같은 입력에는 항상 같은 결과외부 세상과 단절된 형태테스트하기 쉬운 코드Day 11. TDD | 테스트는 [ ] 다TDD: Test Driven Development프로덕션 코드보다 테스트 코드를 먼저 작성하여 테스트가 구현 과정을 주도하도록 하는 방법론테스트와 상호작용하며 발전하는 구현부클라이언트 관점에서의 피드백을 주는 Test Driven애자일 성명서 : http://agilemanifesto.org/테스트는 [ ] 다[ ]는 강의에서😀우리는 항상 팀으로 일한다DisplayName을 섬세하게도메인 용어를 사용하여 한층 추상화된 내용을 담기테스트의 현상을 중점으로 기술하지 말 것BDD 스타일로 작성하기TDD에서 파생된 개발 방법 Given : 시나리오 진행에 필요한 모든 준비 과정 (객체, 값, 상태 등)When : 시나리오 행동 진행Then : 시나리오 진행에 대한 결과 명시, 검증Day 12. 테스트 코드 적용 미션 https://github.com/japygo/readable-code/commits/mission7-1/회고이름 짓기처럼 DisplayName을 짓는 것도 역시나 고민되고 어렵다.테스트 코드를 굳이 작성해야하나 싶은 테스트를 작성해야 하는지 의문이다.테스트 커버리지 보다는 다양한 예외 케이스들을 제대로 했는지가 중요한 것 같다.Day 13. Spring & JPA 기반 테스트: Persistence Layer레이어드 아키텍처(Layered Architecture)와 테스트풍부한 단위 테스트 & 큰 기능 단위를 검증하는 통합 테스트Library vs FrameworkLibrary: 내 코드가 주체Framework: 이미 동작할 수 있는 환경이 있어 내 코드가 수동적Persistence LayerData Access의 역할비즈니스 가공 로직이 포함되어서는 안 된다. Data에 대한 CRUD에만 집중한 레이어Day 14. Spring & JPA 기반 테스트: Business LayerBusiness Layer비즈니스 로직을 구현하는 역할Persistence Layer와의 상호작용(Data를 읽고 쓰는 행위)을 통해 비즈니스 로직을 전개시킨다.트랜잭션을 보장해야 한다.회고테스트 코드 강의 역시 상상 속의 있던 유니콘을 만나는 기분이었다.중요한 걸 알지만 현업에서 만나볼 수 없어서 감이 전혀 오지 않았는데 어떤 느낌인지 알 수 있었다.열심히 해서 다양한 테스트 케이스를 만나 볼 수 있으면 좋겠다.출처https://inf.run/zgJk5https://inf.run/kHiWM

백엔드워밍업클럽테스트

김현규

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

Day 11 리액트 테스트 경험하기평소에 개발할 때는 구현하는데 급해서 테스트 코드를 작성해 본 적이 없었는데 재밌는 경험이었다.전 직장에 근무할때는 Jenkins로 CI/CD를 했었는데, 요즘은 Github Action을 이용해서 배포를 많이 진행하는 것 같다.Github Action은 사용해보진 않았지만 Gitlab의 Webhook 기능을 사용해 Jenkins로 배포를 진행했던 적이 있어서, AWS S3 , Github Action 파트는 큰 어려움이 없었던 것 같다.앞으로 내가 주도적으로 개발을 진행하게 된다면 TDD)를 도입해보는 것도 좋은 선택이 될 것 같다. Day 12 NextJS, 타입스크립트현재 맡은 프로젝트도 NextJS를 사용했기 때문에 좀 더 집중해서 들으려고 했다.팀 프로젝트를 진행할 때 Next.js를 사용하지 않았던 경험이 있는데, 그때 SEO 최적화 작업이 상당히 복잡하게 느껴졌다. (나는 백엔드만 했지만) 그러나 Next.js는 이러한 기능을 기본적으로 지원하므로, 그 부분에서 확실한 장점이 있는 것 같다.현재 맡은 프로젝트에선 AWS Amplify로 Next.js코드를 배포하고, AWS Lambda에서 API를 호출하는데, SSR을 사용하는 경우에 초기 로딩 속도 면에서 더 유리하다.Day 13 리덕스 학습하기 및 리액트 19Redux는 애플리케이션의 전역 상태를 중앙에서 관리해 컴포넌트 간의 상태 공유를 용이하게 한다.현재 맡은 프로젝트에선 Recoil을 사용하고 있는데, 아마 Atomic 패턴을 사용하고 있어서 차용한 것 같다.요즘은 Redux,Recoil 뿐만 아니라 Zustand, Jotai도 상태 관리 라이브러리로 떠오르는 것 같다.과제 정리과제를 강의보다 먼저 진행하다 보니, 초반에 있던 자바스크립트 과제들도 자바스크립트를 사용해서 진행하는 줄 모르고 모든 과제를 React + TypeScript로 진행했다.1. 음식 메뉴 앱2. 가위 바위 보 앱useState를 사용해 상태관리를 했다.플레이어가 선택한 값을 상태에 저장하고, 컴퓨터는 무작위로 선택을 한다.총 10라운드가 끝나면 결과를 보여주고, 재시작 버튼을 눌러 게임을 다시 시작할 수 있다.3. 퀴즈 앱handleOptionClick으로 사용자가 선택지를 클릭하면, 해당 옵션을 선택하고 정답 여부를 확인한다.handleNext로 새로운 퀴즈를 생성하고 상태를 초기화 한다.css를 평소에 해본적 없어서 이것저것 해봤는데 지금와서 보니까 진짜 보기 싫다..4. 책 리스트 나열 앱책 이름, 책 저자를 입력하면 제출 버튼이 활성화된다.책을 등록하면 책이 추가되었다는 알림이 5초동안 뜬다.삭제 버튼을 누르면 책 리스트에서 책이 삭제되고 삭제 되었다는 알림이 5초동안 뜬다.useEffect 훅을 사용해서 상태가 변경될때 5초 후에 자동으로 알림 메시지를 제거했다.5. Github Finder 앱Axios로 Github API를 호출해 프로필 정보, 저장소 목록을 가져온다.입력 필드에 닉네임을 입력하면 실시간으로 정보를 가져온다.Github Token 없이 API를 호출하면 시간당 60개의 API밖에 호출을 못해서, 환경 변수로 깃허브 토큰을 따로 등록해서 사용했다.과제 6,7,8[인프런 워밍업 클럽 2기 FE] 2주차 발자국2주차 발자국에 정리해놨다.9. 디즈니 플러스 앱모든 과제중 시간이 제일 오래 걸렸다..알고보니 리액트 강의에 Netflix 사이트를 만들어보는게 있었다... 그걸 보기 전에 혼자서 진행해서 더 어려웠던 것 같다.CSS도 계속 어긋나고.. 우여곡절이 많았는데 결국 외적으로 보이는 부분은 상당 부분 포기했다.Google OAuth로 로그인을 하고, Google OAuth 서버에서 토큰을 발급 받아왔다.받아온 엑세스 토큰으로 사용자의 프로필 정보를 가져와 오른쪽 상단에 프로필 이미지를 화면에 보여줬다.tmdb 에서 토큰을 발급받아 영화 정보를 가져왔다.10. 포켓몬 도감 앱모든 과제중 제일 공들여서 작업했고, 아는 주제라 제일 재밌었다.초기에 40개의 포켓몬을 불러오고, 포켓몬 색깔에 맞춰 디자인했다. 또 타입별로 색깔을 지정해줬다.포켓몬 색깔이나 타입의 색깔이 흰색일 때는 가시성을 위해 폰트 색상을 검정색으로 변경해줬다.const getPokemonColor = (colorName: string) => { const colorMap: { [key: string]: string } = { red: "#F08030", blue: "#6890F0", green: "#78C850", yellow: "#F8D030", brown: "#A52A2A", purple: "#A040A0", pink: "#EE99AC", gray: "#B8B8D0", white: "#F5F5F5", black: "#705848", default: "#A8A878", }; return colorMap[colorName] || colorMap.default; }; const typeColors: { [key: string]: string } = { grass: "#78C850", poison: "#A040A0", fire: "#F08030", water: "#6890F0", bug: "#A8B820", normal: "#A8A878", flying: "#A890F0", electric: "#F8D030", ground: "#E0C068", psychic: "#F85888", rock: "#B8A038", ice: "#98D8D8", dragon: "#7038F8", dark: "#705848", steel: "#B8B8D0", fairy: "#EE99AC", };react-chartjs-2 라이브러리를 활용해 포켓몬의 능력치를 육각형 차트로 표현했다.포켓몬 타입 상성에 대해서는, 공격,방어를 나눠서 보여줬다.11. 퀴즈 앱(리액트)과제중 처음으로 Next.js를 사용했다. Question 페이지에서는 랜덤한 퀴즈 두개를 제시하고 즉시 정답을 확인할 수 있다.State 페이지에서는 카테고리를 고르고 그 카테고리 내에서 랜덤한 퀴즈 두개를 제시하고 즉시 정답을 확인할 수 있다.Quiz 페이지에서는 카테고리를 고르고 테스트를 시작하고 총 4문제를 제시한다. 3문제 이상 맞출시 합격, 그 외에는 불합격으로 표시하고 재시작 버튼으로 다시 테스트를 볼 수 있다.12. 쇼핑몰 앱 장바구니에 담기 버튼을 누르면 실시간으로 장바구니 카트 모양에 숫자가 올라간다.제품 이미지를 클릭하면 제품 상세 페이지로 넘어가고, 장바구니에 담기, 장바구니로 이동하는 버튼이 존재한다.장바구니에서 +,- 버튼을 누르면 갯수가 증감하고 1일때 -를 누르면 리스트에서 삭제된다.Redux를 활용해 상태관리를 진행했다.후기이렇게 총 3주간의 인프런 워밍업 클럽이 마무리되었다.다행히 회사 업무가 아직 여유로워서, 업무 중간중간 과제를 수행할 수 있었다. 채팅 앱과 Note 앱을 제외한 대부분의 과제를 완수했지만, 다른 분들의 과제를 보니 내 작업의 퀄리티가 확실히 부족하다는 생각이 들었다.워밍업 클럽 이전에는 프론트엔드 경험이 전무했으나, 이번 기회를 통해 다양한 것들을 만들어보며 좋은 경험을 쌓았다. 곧 시작될 새로운 프로젝트에서 Node와 React를 맡게 되었는데, 워밍업 클럽에서의 경험 덕분에 조금은 자신감이 붙은 것 같다.

김예지

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

 따라하며 배우는 리액트 A-Z 마무리섹션 7~8 TDD 리액트 테스트 경험하기테스트 코드를 작성하면 좋은 점 : 많은 기능을 테스트 하기에 소스 코드에 안정감이 부여된다. 실제 개발하면서 많은 시간이 소요되는 부분은 디버깅 부분이라 TDD를 사용하면 디버깅 시간이 줄어든다. 코드를 더욱 신중하게 짤 수 있다.React Testing Library : create-react-app로 생성된 프로젝트는 이미 지원하는 라이브러리. DOM Node를 테스트하기 위한 매우 가벼운 솔루션이다. 섹션 9 Next.js와 TypeScript- Next.js 대해 알아보기리액트의 SSR을 쉽게 구현할 수 있게 도와주는 간단한 프레임워크리액트로 개발할 때, SPA를 사용하여 CSR(Client-Side Rendering)을 적용하면 장점이 많지만, 단점도 있다. 그 중 하나가 SEO 최적화 문제. CSR 방식에서는 첫 페이지에서 빈 HTML 파일을 불러오고, 자바스크립트 파일을 해석해 화면을 구성하기 때문에 검색 엔진에 제대로 노출되지 않을 가능성이 크다. → 리액트에서도 SSR을 지원하지만 구현하기에 복잡하다.Pre-rendering : 모든 페이지에 대한 HTML을 미리 생성하여 클라이언트 사이드에서 자바스크립트로 처리하기 전에 브라우저에 전달하는 방식. 검색 엔진 크롤러가 자바스크립트를 실행하지 않고도 완전한 HTML 콘텐츠를 확인할 수 있기 때문에 SEO(Search Engine Optimization) 최적화에 매우 유리. (페이지가 미리 렌더링되므로 검색 엔진이 더 쉽게 콘텐츠를 인덱싱하고, 검색 순위가 개선될 가능성이 커진다.)Data FetchinggetStaticProps (정적 생성) : 빌드 시에 데이터를 미리 가져와서 HTML을 생성하는 방식. 정적 콘텐츠를 사전에 생성 → 페이지 로딩 속도가 빠르고 서버 부하가 적다. 주로 콘텐츠가 자주 변경되지 않는 경우에 적합getServerSideProps(서버사이드 렌더링) : 페이지 요청 시 서버에서 데이터를 가져와 동적으로 HTML을 생성하는 방식. 실시간 데이터가 필요한 경우나 사용자별로 다른 데이터를 제공해야 할 때 유용. (ex. 동적 라우팅이 필요한 경우 (pages/post/[id].js)에 활용.)클라이언트 사이드 데이터 페칭 (Client-side Data Fetching) : 페이지가 로드된 후 클라이언트에서 데이터를 가져오는 방식. Server Side Rendering(SSR)과 달리, 초기 HTML이 로드된 후 추가적인 API 요청을 통해 데이터를 온다. 실시간 업데이트, 인터랙티브 콘텐츠에 사용.fallbackgetStaticPaths로 미리 정의된 경로 외에는 모두 404 페이지로 처리getStaticPaths로 미리 생성되지 않은 경로는 첫 번째 요청 시 "fallback" 상태가 표시되며, 해당 페이지가 서버에서 동적으로 생성. - TypeScript 대해 알아보기타입스크립트(TypeScript)는 자바스크립트에 타입을 추가하여 더 안전하고 유지보수하기 쉬운 코드를 작성할 수 있게 만든 확장된 프로그래밍 언어입니다. 자바스크립트의 모든 기능을 포함하면서도, 정적 타입을 지원해 코드 작성 중에 타입 오류를 미리 확인할 수 있습니다.타입스크립트의 타입any : 어떤 타입이든 할당할 수 있는 타입Union : 둘 이상의 타입을 사용할 때 사용하는 타입Enum : 값들의 집합에 이름을 붙인 타입. enum은 선언된 후 수정할 수 없으며, 값은 숫자 또는 문자열만 허용Void : 함수에서 반환값이 없을 때 사용되는 타입. 일반적으로 undefined나 null 값을 가질 수 있지만 의미상 값이 없는 경우를 나타냄.Never :  절대 발생하지 않는 값을 나타내는 타입. 어떤 값도 가질 수 없다.    - Nextjs13Static Data fetching next에서 fetch 함수는 는 자동으로 데이터를 가져오고 캐시한다.Refresh on every request 데이터가 캐시가 안되게 하고 모든 리퀘스트마다 다시 가져올 수 있게 한다. getServerSideProps유사Revalidating Data 캐시된 데이터를 일정 시간 간격으로 재 검증하는 하는 방법fenerateStaticParams generateStaticParams는 getStaticPaths와 유사하게 동작하며 특정 페이지에서 정적으로 생성될 때 경로를 지정Server Action 서버 액션을 통해 클라이언트 컴포넌트에서 서버 작업을 바로 처리할 수 있다. 자바스크립트가 비활성화된 환경에서도 서버 액션을 사용할 수 있도록 지원Form Action : action={} 속성은 HTML 폼에서 서버로 데이터를 전송할 때 사용Parallel Routes : 여러 경로를 동시에 처리하고, 페이지 간의 독립적인 렌더링을 기능.default.tsx : 페이지에서 기본적으로 제공되는 파일. 특정 경로에 대한 기본적인 레이아웃을 제공.[...catchAll] : 어떤 경우로 들어오더라도 이 컴포넌트가 렌더링 된다.Image CommponentIntercepting Routes : 바뀐 경로로 모달을 띄울 수 있음.Partial Pre-rendering (PPR) : 정적 컴포넌트와 동적 컴포넌트 함께 사용. ex) 검색input과 검색 결과 창Image Component : image 태그를 사용하면 자동으로 제공되는 props가 있다. 빌드할 때 이미지를 최적화 해줌placeholder="blur" 원본이미지가 다운로드 되기전에 블러된 저해상도 이미지가 보임blurDataUrl : 로딩될동안 미리 보여줄 이미지 그냥 색상 코드도 입력 가능Remote Images 외부에서 가져오는 이미지는 widthl, height,를 줘야한다. 그리고 config 에서 remotePatterns도 설정해줘야 함이미지를 가져오는 동안에 들어갈 높이와, 넓이를 쓰면 레이아웃 쉬프트(Layout Shift) 방지할수있다.이미지랑 높이를모르면? fill 사용하면 부모높이를 전체 차지Responsive : 반응형 이미지 처리sizes 값은 next.js 이미지에 의해서 자동으로 생성된 이미지들 중에서 어떤 이미지를 다운로드할지 브라우저가 결정하는데 도움을 준다.next13의  Image Commponent 부분이 좋았다. 아무래도 이미지는 웹에서 신경써야 할 부분이 많은데 알아서 이미지를 최적화 해준다거나.. blur기능이라던가 다음에 next에서 이미지를 써야 한다면 img 말고 컴포넌트로 써봐야겠다. 섹션10 리액트 18버전 Automatic Batching (자동 배칭) : 여러 상태 업데이트를 하나의 그룹으로 묶어 한 번에 렌더링을 처리. 기존에는 이벤트 핸들러에서만 자동 배칭이 적용되었지만, React 18에서는 API 요청이나 타임아웃 함수에서도 이 기능이 확장되었다. 불필요한 여러 번의 렌더링을 줄여 성능이 향상.Suspense on the Server : 서버 사이드 렌더링에서 Suspense를 사용할 수 있게 됨. Suspense는 컴포넌트를 더 작은 독립적인 단위로 분할. 사용자가 모든 데이터를 기다리지 않고 일부 콘텐츠를 더 빠르게 볼 수 있다.Transitions (트랜지션) : Transition을 도입하여 상태 업데이트의 우선순위를 관리할 수 있게 됨. 즉각적인 응답이 필요한 업데이트와 그렇지 않은 업데이트 분리 가능.  ex) 검색창: 사용자가 검색어를 입력하면 즉각적으로 업데이트가 필요하다.검색 결과 창: 검색 결과를 로드하는 동안 트랜지션을 적용해 부드럽게 업데이트할 수 있다. 사용자는 검색어 입력에는 즉각 반응을 보고, 결과는 천천히 업데이트되더라도 매끄럽게 경험할 수 있다. 섹션 11 리덕스에 대해서리덕스는 자바스크립트 애플리케이션을 위한 상태 관리 라이브러리다.리덕스(Redux)의 데이터 플로우: 액션(Action)이 발행되면 → 리듀서(Reducer) 호출 → 스토어(Store) 내부 상태가 업데이트 → 새로운 상태에 따라 컴포넌트가 다시 렌더링Sub Reducer: 모든 상태 관리를 하나의 리듀서에서 처리하는 것이 비효율적이고 유지 보수가 어려워지기 때문에, 여러 작은 리듀서들로 나누어 상태를 관리하는 방식 combineReducers로 합칠 수 있다.Redux Middleware: 액션(Action)이 디스패치(Dispatch)된 후 리듀서에 도달하기 전에 중간에서 지정된 작업을 실행할 수 있게 해주는 중간자. 로깅, 충돌 보고, 비동기 API와 통신, 라우팅 등을 위해 리덕스 미들웨어를 사용한다.Redux Thunk 사용하는 이유 : 리덕스에서 비동기 작업을 처리하기 위해서Redux Toolkit: 리덕스 툴킷은 리덕스 로직을 작성하기 위한 권장 접근 방식이다.리액트의 불변성 지키기 처럼 리덕스도 state를 직접적으로 변경하지 않는다.리덕스 툴킷 APIs :빌더 콜백 (Builder Callback) : addCase 특정 액션 타입에 맵핑된 리듀서 함수 추가, addMatcher 새로 들어오는 모든 액션에 대해서 주어진 패턴과 일치하는지 확인하고 리듀서를 실행, addDefaultCase 다른 케이스 리듀서나 매처 리듀서가 실행되지 않았다면 기본 케이스 리듀서가 실행 된다맵 오브젝트(Map Object) : Initial State 리듀서를 처음 호출할 때 사용하는 초기 값, Action Map 액션 타입이 케이스 리듀서에 맵핑되어 있는 객체, Case Reducer 이 작업에 대해 리듀서 및 매처 리듀서가 실행되지 않는 경우 실행되는 기본 케이스 리듀서 섹션 12 도커 체험하기어떠한 프로그램을 다운 받는 과정을 간단하게 만들기 위해서.컨테이너를 사용하여 응용프로그램을 더 쉽게 만들고 배포하고 실행할 수 있도록 설계된 도구. 컨테이너 기반의 오픈소스 가상화 플랫폼.  섹션 13 리액트 19use() Hooks : 비동기 요청으로 데이터를 가져오는 새로 나온 Hook. Actions : 클라이언트-서버 간 데이터 처리를 간소화.useFormStatus : 폼의 상태를 추적하는 데 사용되는 훅useActionState : Action을 편하게 쓰기 위해 도입useOptimistic: 새로 도입된 훅. 인터페이스가 즉각적으로 반응하는 것처럼 보이게 할 수 있다.Mata tags: 과거에는 수동으로 삽입하거나 라이브러리를 사용했으나 이제 컴포넌트 자체에서 바로 사용할 수 있다.React Server Component: 서버에서 렌더링되고 클라이언트에 전송되는 리액트 컴포넌트. 3주차 과제포켓몬 도감 앱 만들기 (Post Link)github : Link 퀴즈 앱 만들기 (Post Link)github : Link  리덕스를 이용한 쇼핑몰 만들기 (Post Link)github : Link 3주차 회고시간이 정말 빠르다. 벌써 3주 차 회고를 하고 있다. 스터디 신청한지 얼마 안 된 거 같은데 벌써 3주나 지났다니..처음 목표는 강의 100% 수강과 모든 미션 완료였다. 10월에는 휴일도 좀 있어서 할 수 있을지도 모른다고 생각했는데.. 모든 걸 다 하려니 시간이 모자라긴 했다. 3주 차에 있는 Day 12 채팅 앱, Day 13 Note 앱 미션은 하지 못해서 아쉽다. 3주 차 미션 중에서는 이 두 미션이 재미있어 보였고 꼭 하고 싶다고 생각했는데, 시간이 오래 걸릴 것 같아 일단 제외했다. 완료하지 못한 미션은 스터디가 끝나고 개인적으로 진행할 생각이다.자바스크립트 부분도 코치님한테 받았던 코드랑 작성했던 코드를 비교해서 보고 싶었는데 3주차에는 시간이 없어서 스터디 끝난 후로 미뤘다.마지막으로 진행했던 쇼핑몰 미션은 익숙하지 않은 타입 스크립트에 리덕스를 함께 사용하다 보니 생각보다 시간이 오래 걸렸다. 중간 점검 때 코치님이 리덕스 어렵다고 하셨는데 정말 어려웠다. 그래도 어떻게 미션은 완료했다.시간이 부족해 아쉬운 점도 있지만 퇴근 후 집에 와서 3주 동안 바쁘게 집중할 수 있었던 경험은 즐겁고 유익한 시간이었다. 남은 10월에는 위에서 말한 것처럼 완료 못한 미션이나 코드 비교를 하며 공부하는 시간을 가질 생각이다.커리큘럼 마지막 날인 오늘 이렇게 3주 차 발자국을 마무리한다.

워밍업클럽

김예지

[인프런 워밍업 스터디 클럽 2기 FE] 3주차 과제 - 쇼핑몰 앱 만들기

리덕스를 이용한 쇼핑몰 앱 만들기github : 12-shopping-mall 기능목록json 데이터로 상품 리스트 출력카테고리 별 상품 출력하기상품 디테일 페이지장바구니 기능 구현하기📁public ├── products.json 📁src ├── App.tsx ├── index.tsx ├── 📁app │ └── store.ts ├── 📁components │ ├── Nav.tsx │ └── Products.tsx ├── 📁features ├──── 📁products │ └── productsSlice.ts ├──── 📁cart │ └── cartSlice.ts ├── 📁pages │ ├── Cart.tsx │ └── Detail.tsx productsSlice.ts : 상품 데이터 관리 (데이터 가져오기)// Product 타입 정의 export interface Product { category: string; name: string; price: number; description: string; } // ProductsState 타입 정의 interface ProductsState { items: Product[]; // 배열로 설정 status: 'idle' | 'loading' | 'succeeded' | 'failed'; } // 초기 상태 설정 const initialState: ProductsState = { items: [], // 빈 배열로 시작 status: 'idle', };'idle': 초기 상태로, 데이터 요청이 시작되지 않았음을 나타냄'loading': 상품 데이터를 요청 중임을 나타낸다. 로딩 애니메이션이나 메시지를 표시할 수 있다.'succeeded': 상품 데이터 요청이 성공. 데이터 화면에 표시 가능'failed': 상품 데이터 요청이 실패. 오류 메시지를 표시 가능 초기 상태 설정 initialState: 리듀서에서 상태를 초기화하는데 사용 애플리케이션이 시작될 때 어떤 상태를 가지는지 정의. 데이터 요청이 이루어지기 전에는 빈 배열과 idle상태를 가지고 있다.  이후 데이터 요청이 진행되고, 성공적으로 완료되면 상태가 업데이트 됨 Thunk 함수 정의 : 비동기 데이터 가져오기export const fetchProducts = createAsyncThunk<Product[]>( 'products/fetchProducts', async () => { const response = await fetch('/products.json'); // public 폴더의 JSON 파일을 비동기로 가져옴 const data = await response.json(); // JSON 데이터를 파싱 return data; // Thunk는 이 데이터를 fulfilled 상태로 반환 } );createAsyncThunk 사용할 때 자동으로 생성되는 액션 타입'{sliceName}/{thunkName}/pending' → products/fetchProducts/pending'{sliceName}/{thunkName}/fulfilled' → products/fetchProducts/fulfilled'{sliceName}/{thunkName}/rejected' → products/fetchProducts/rejected  items 상품을 여러 개의 상품을 저장할 수 있는 배열 fetchProducts 의 비동기 작업이 성공적으로 완료되었을 때,/public/products.json에서 가져온 상품 데이터를 저장. Redux 슬라이스 정의const productsSlice = createSlice({ name: 'products', //리듀서 이름 initialState, // 데이터 초기 reducers: {}, // 상태가 변하면 어떻게 실행될지 extraReducers: (builder) => { builder .addCase(fetchProducts.pending, (state) => { state.status = 'loading'; // 로딩 상태 처리 }) .addCase(fetchProducts.fulfilled, (state, action) => {//데이터 요청 성공 state.status = 'succeeded'; //상태 업데이트 state.items = action.payload; // 받아온 데이터를 items에 저장 }) .addCase(fetchProducts.rejected, (state) => { state.status = 'failed'; // 실패 상태 처리 }); }, })productsSlice = createSlice : 리듀서 함수 정의initialState 위에 설정한 초기 상태 items: [] / status: 'idle'state는 Redux Toolkit의 createSlice를 사용하여 정의된 리듀서 함수에서 전달되는 매개변수로, 현재 상태를 나타낸다. 다시 정리하는 코드 흐름 :이해한 내용을 바탕으로 리덕스 데이터 플로우를 그려 봤다.초기 상태 const initialState: ProductsState = {} 에서 status는 'idle'로 설정. 이 상태는 비동기 요청이 시작되기 전.비동기 요청 시작 Products.tsx의 useEffect 훅에서 status가 'idle'일 때 dispatch(fetchProducts())를 호출 →const fetchProducts = createAsyncThunk<Product[]> 실행.데이터 요청 fetchProducts thunk는 /products.json 파일에서 상품 데이터를 비동기로 가져온다.응답 처리 요청 성공하면, Redux의 상태가 fulfilled로 업데이트.최종 상태 이때 status는 'succeeded'로 바뀌고, items 배열에는 JSON 파일에서 가져온 상품 목록이 저장됨. Products.tsx // 컴포넌트가 처음 마운트될 데이터 비동기 요청 useEffect(() => { if (status === 'idle') { dispatch(fetchProducts()); } }, [dispatch, status]); const filteredProducts = products.filter((product) => { if (selectedCategory === '모두') return true; if (selectedCategory === '전자기기') return product.category === 'electronics'; if (selectedCategory === '쥬얼리') return product.category === 'jewelry'; if (selectedCategory === '남성의류') return product.category === 'men'; if (selectedCategory === '여성의류') return product.category === 'women'; return false; }); 선택된 카테고리 따라 필터링 된 제품 리스트를 반환해준다. 원래는 spread operator로 데이터를 반환했는데 2주차때 작성했던 발자국을 보니 filter도 원본 배열을 수정하지 않고 새로운 배열을 반환해주는 메서드라 filter를 사용했다. cartSlice.ts : 장바구니 상태 관리 (상태 변경 및 업데이트)// cart 타입 정의 interface CartItem { id: string; name: string; category: string; price: number; quantity: number; } //CartState 타입 정의 interface CartState { items: CartItem[]; } //초기 상태 const initialState: CartState = { items: [], }; const cartSlice = createSlice({ name: 'cart', initialState, reducers: { addItem: (state, action: PayloadAction<CartItem>) => { // 장바구니에 아이템 추가 state는 initialState const existingItem = state.items.find( (item) => item.id === action.payload.id ); // 기존에 같은 아이템이 있는지 찾기 if (existingItem) { existingItem.quantity += 1; // 이미 존재하면 + 1 } else { // 존재하지 않으면 새로 추가하고 수량은 1로 설정 state.items.push({ ...action.payload, quantity: 1 }); } }, increaseQuantity: (state, action: PayloadAction<string>) => { // 특정 아이템의 수량 1 증가 const item = state.items.find((item) => item.id === action.payload); if (item) item.quantity += 1; // 해당 아이템이 있으면 수량을 증가시킴 }, decreaseQuantity: (state, action: PayloadAction<string>) => { // 특정 아이템의 수량 1 감소 const item = state.items.find((item) => item.id === action.payload); if (item && item.quantity > 1) item.quantity -= 1; // 아이템이 있고 수량이 1 이상일 때만 감소 }, removeItem: (state, action: PayloadAction<string>) => { // 특정 아이템을 장바구니에서 제거 state.items = state.items.filter((item) => item.id !== action.payload); // 해당 아이템의 id와 일치하지 않는 아이템들로 배열을 갱신 }, }, }); export const { addItem, increaseQuantity, decreaseQuantity, removeItem } = cartSlice.actions; // 정의한 리듀서에 해당하는 액션 생성자들을 export export default cartSlice.reducer;* productsSlice는 데이터를 가져와 저장하는 용도로 상태를 직접 변경하지 않기 때문에 reducers 값이 비어있지만 cartSlice는 사용자의 동작에 따라 상태가 동적으로 변하기 때문에 reducers를 사용한다. 상품 클릭 시 /detail 페이지 이동 + 상품 장바구니 추가 기능 const { id } = useParams<{ id: string }>(); // URL에서 id 파라미터 가져오기 const dispatch = useDispatch<AppDispatch>();상품을 클릭하면 상품 페이지를 보여준다. const product = useSelector((state: RootState) => state.products.items.find((item) => item.id === id) );useParams를 통해 URL의 id 파라미터를 가져온다.Redux의 useSelector를 사용해 products 슬라이스에서 해당 상품(id)을 검색 → 일치하는 데이터 가져옴 const isInCart = useSelector((state: RootState) => state.cart.items.some((item) => item.id === id) );useSelector를 통해 cart 슬라이스에서 해당 상품이 장바구니에 있는지 확인some() 메서드를 사용해 장바구니에 같은 id의 상품이 있는지 여부를 반환<div className='bottom'> {/* 장바구니에 있는 경우 텍스트 변경 */} {isInCart ? ( <button type='button' onClick={handleAddToCart}> 이미 장바구니에 담긴 상품 </button> ) : ( <button type='button' onClick={handleAddToCart}> 장바구니 추가 </button> )} <button type='button' onClick={handleGoToCart}> 장바구니로 이동 </button> </div> const handleAddToCart = () => { const cartItem = { ...product, quantity: 1 }; // quantity 추가 dispatch(addItem(cartItem)); // Redux에 추가 }; 장바구니 추가 핸들러quantity 속성을 1로 설정하여 Redux 상태에 저장 dispatch를 통해 addItem 액션을 호출해 장바구니 상태를 업데이트 /cart 장바구니 페이지const dispatch = useDispatch<AppDispatch>(); const cartItems = useSelector((state: RootState) => state.cart.items);Redux 스토어의 cart.items 배열을 가져온다.<button onClick={() => dispatch(decreaseQuantity(item.id))}> - </button> <input value={item.quantity} readOnly /> <button onClick={() => dispatch(increaseQuantity(item.id))}> + </button> <button className='delete' onClick={() => dispatch(removeItem(item.id))}>삭제</button>수량조절 & 삭제 버튼수량 버튼은 수량이 1 이하로 감소하지 않도록 cartSlice에서 설정한다.if (cartItems.length === 0) { return ( <div className='wrap cart'> <p className='none'>장바구니가 비어있습니다.</p> </div> ); }아이템이 하나도 없을 때는 장바구니가 비었다는 문구를 보여준다.  작성할 때는 코드가 다 까만 배경이었는데 저장하고 나니까 이상하게 보인다 😥 마지막 제시간에 제출하기 위해 급하게 하다 보니 이미지도 넣지 못했다. 상품 목록 불러올 때 일부러 지연 시킨 다음에 사진이 로드 되기 전 UI도 구현하는 부분이랑 cart hover 했을 때 장바구니 아이템 보여주는 부분도 생략했다. 스터디는 이렇게 제출하지만 따로 추가해보려고 한다.  

워밍업클럽

채널톡 아이콘