블로그

[3주차 발자국] 스프링과 테스트 코드

인프런 ‘Readable Code: 읽기 좋은 코드를 작성하는 사고법’을 수강한 후, 작성한 내용입니다.📌 3주차 강의Spring & JPA 기반 테스트레이어드 아키텍처와 테스트레이어를 구분하는 이유 ⇒ 관심사의 분리!스프링과 JPA라는 기술 자체가 중요한 것이 아니라,무엇을 어떻게 테스트 할 것인지가 중요하다.통합 테스트여러 모듈이 협력하는 기능을 통합적으로 검증단위 테스트만으로는 기능 전체의 신뢰성을 보장하기 어렵다.풍부한 단위 테스트 & 큰 기능 단위를 검증하는 통합 테스트Spring / JPA 훑어보기 & 기본 엔티티 설계Library vs Framework라이브러리는 내 코드가 주체가 된다.프레임워크에서는 코드를 작성해서 끼워넣는다.SpringIoC : 객체의 생명주기에 대한 관리를 제 3자가 한다.DI : 컨테이너가 주입해준 객체를 사용한다.AOP : 코드 상으로 흩어져있는 부가적인 앞뒤로 해주는 작업을 한 군데로 모은다.JPAORM객체 지향 패러다임과 관계형 DB 패러다임의 불일치를 해결개발자는 단순 작업을 줄이고, 비즈니스 로직에 집중할 수 있다.인터페이스이고, 여러 구현체 중 Hibernate를 많이 사용한다.스프링 진영에서는 JPA를 한번 더 추상화한 Spring Data JPA 제공Persistence Layer 테스트쿼리가 명확한데 굳이 테스트를 작성해야 할까?where 조건이 많아서 쿼리가 길어진다거나, 구현 기술이나 방법이 변경되는 상황에서도 보장할 수 있다.작성한 코드가 제대로 된 쿼리가 날라가는지에 대한 보장미래에 어떤 형태로 변형될지 모르기에 이에 대한 보장Persistence LayerData Access 역할비즈니스 가공 로직이 포함되어서는 안된다.Repository 테스트는 단위 테스트 성격에 가깝다.데이터베이스에 접근하는 로직만 가지고 있기 때문DataJpaTest vs SpringBootTestDataJpaTest는 SpringBootTest보다 가볍다.JPA 관련 빈들만 주입한다.// 리스트 테스트 시 사용하면 좋다! assertThat(products).hasSize(2) .extracting("productNumber", "name", "sellingStatus") .containsExactlyInAnyOrder( tuple("001", "아메리카노", SELLING), tuple("002", "카페라떼", HOLD)); .extracting()검증하고자 하는 필드 추출@ActiveProfiles("test")test 프로파일로 적용Business Layer 테스트Business Layer비즈니스 로직을 구현하는 역할Persistence Layer와의 상호작용을 통해 비즈니스 로직 전개트랜잭션을 보장@DataJpaTest내부에 @Transactional이 존재@SpringBootTest + @Transactional이 조합이 가질 수 있는 문제?프로덕션 코드에 있는 트랜잭션 경계가 모호해질 수 있다.실제로 트랜잭션 설정이 되어있지 않았다면?간단한 것들도 테스트를 해야할까?미래 시점에 대한 대비를 생각하여 작성하자!수량 차감에 대한 비즈니스 로직수량을 차감할 수 있는 조건이 만족하면,수량을 차감한다. (stock.deductQuantity())수량을 차감할 수 있다면,차감한다.수량에 대한 검증 로직을 두 곳에서 다 해야하나?Stock은 비즈니스 로직이 어떻게 되어있는지 모르기 때문에 이에 대한 보장을 해야한다!재고 감소는 동시성 문제를 고려해야 한다. 동시에 같은 재고 개수를 읽으면 문제가 생기기에 보통 lock을 사용한다.Presentation Layer 테스트Presentation Layer외부 세계의 요청을 가장 먼저 받는 계층파라미터에 대한 최소한의 검증을 수행넘겨온 값들에 대한 검증하위 레이어는 Mocking 처리의존관계를 가짜로 처리하여 테스트 대상에 집중💡CQRS란?Command와 Query를 분리하여 서로 연관이 없게 만든다.DB에 대한 엔드포인트를 구분하여 Query용 DB와 Write용 DB를 나누어 쓸 수 있다. (Master/Slave)📌 Day 11 미션 : 단위 테스트 작성하기Day 11 미션은 Readable Code 강의에서 진행한 프로젝트의 테스트 코드를 작성하는 것이다. 테스트에 대해 참고할 조언은 다음과 같다.테스트가 필요하다고 판단하는 과정부터 시작이다.어떤 클래스, 메서드를 테스트하고 싶은지 명확한 이유와 함께 고민해보자.가장 작은 단위의 메서드부터 테스트를 작성해보자.https://github.com/hgh1472/readable-code테스트를 작성해보며 느낀 점은 외부 세계를 분리할수록 테스트를 작성하기 쉽다는 것이다.한가지 예를 들면, 메서드 중 콘솔로 유저 입력을 받고 입력에 해당하는 도메인으로 바꿔주는 메서드가 존재했다. 이 메서드를 테스트하려면 어떻게 해야할까?유저 입력을 System.setIn() 메서드를 통해 미리 준비시키고 테스트를 진행해야 했다. 그런데 InputHandler 내부에서 Scanner를 정적 필드로 가지고 있기 때문에, 한 번에 여러 테스트에 대한 입력이 적용되지 않아서 테스트하는 데에 큰 불편함이 존재했다.즉, 테스트 코드를 작성하면서 프로덕션 코드의 리팩토링까지 생각이 이어질 수 있는 것이다. 테스트 코드는 단순히 기능을 테스트하기 위함 뿐만 아니라, 프로덕션 코드와 상호보완적으로 리팩토링의 포인트가 될 수 있다는 점을 느꼈다!

백엔드테스트코드

codestudy

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

Netflix 클론코딩 - 영화 검색 서비스 1. 프로젝트 기본 설정 및 구조기술 스택Next.js 기반 프레임워크React 컴포넌트 시스템TypeScript 타입 정의Tailwind CSS 스타일링Supabase 데이터베이스TMDB API (영화 데이터 소스)프로젝트 구조config, app, components, utils 폴더 구성페이지 라우팅 설정환경 변수(.env) 구성2. 데이터베이스 구축Supabase 설정TMDB에서 가져온 60개의 영화 데이터 활용CSV 파일 업로드 방식으로 데이터 임포트영화 테이블 스키마 구성:제목, 설명, 이미지 URL, 평점, 인기도, 개봉일 등데이터 타입 설정 (float, string, nullable 등)3. UI 컴포넌트 개발헤더 컴포넌트상단 고정 네비게이션 바로고 및 네비게이션 메뉴 (Movies, Dramas)검색 기능 UI 구현푸터 컴포넌트하단 고정 레이아웃저작권 및 출처 정보 표시영화 카드 컴포넌트그리드 시스템으로 반응형 레이아웃 구현MD 사이즈에서 4개, 기본 사이즈에서 3개 카드 표시호버 효과 및 트랜지션 적용영화 상세 페이지동적 라우팅을 통한 개별 영화 페이지영화 포스터, 제목, 설명, 평점, 인기도, 개봉일 표시4. 데이터 관리 및 API 연동React Query 활용useQuery hook으로 데이터 페칭 및 캐싱로딩 상태 및 에러 처리Recoil 상태 관리atom을 활용한 검색어 상태 관리useRecoilState, useRecoilValue 등의 훅 활용Supabase API 연동영화 목록 조회 (getMovies)개별 영화 상세 정보 조회 (getMovie)검색 기능 구현 (SearchMovies)5. 고급 기능 구현무한 스크롤React Intersection Observer 활용useInfiniteQuery로 페이지네이션 구현Range Query를 통한 효율적인 데이터 로딩페이지 단위로 12개 항목씩 로드검색 기능실시간 검색어 상태 관리Supabase LIKE 쿼리를 활용한 검색 기능검색 결과 렌더링데이터 최적화flatten을 통한 배열 데이터 처리페이지 상태 관리 (hasNextPage, isFetching 등)6. SEO 최적화메타데이터 관리generateMetadata 함수 구현동적 메타데이터 생성영화 제목, 설명, 이미지 정보 포함소셜 미디어 공유 최적화오픈 그래프 태그 추가트위터 카드 설정카카오톡 공유 시 이미지와 설명 표시 최적화7. 학습 회고이 Netflix 클론 프로젝트를 통해 Next.js의 App Router와 TypeScript, Supabase를 결합한 풀스택 개발을 경험했습니다. 컴포넌트 아키텍처: 서버 컴포넌트와 클라이언트 컴포넌트를 적절히 분리하는 Next.js의 패턴을 익힘데이터 페칭 최적화: React Query를 활용한 효율적인 데이터 관리와 무한 스크롤 구현 방법 서버리스 백엔드: Supabase를 활용한 데이터베이스 관리와 서버 액션으로 별도 백엔드 없이 기능 구현복습을 꾸준히 하고 supabase의 다양한 기능을 습득하도록 해야겠습니다.  🛠 미션 해결 과정찜 기능 구현 과정 및 회고구현 과정Supabase 테이블 설계:favorites 테이블 생성: id, movie_id, device_id, created_at 필드 포함Foreign key는 설정하지 않고 간단한 구조로 진행클라이언트-사이드 식별자 구현:deviceId.ts 유틸리티 생성: 사용자 브라우저를 식별하기 위한 고유 ID 생성 및 로컬 스토리지에 저장로그인 없이도 각 사용자의 즐겨찾기를 식별할 수 있는 방법 제공서버 액션 구현:favoriteActions.ts: 즐겨찾기 추가/제거 및 목록 조회 기능 구현searchMovies 함수 수정: 즐겨찾기 정보 포함 및 즐겨찾기 항목 상단 정렬 로직 추가UI 컴포넌트 수정:MovieCard 컴포넌트에 하트 아이콘 추가: Font Awesome과 Material Tailwind 사용찜 상태에 따라 아이콘 스타일 변경 (빨간색/회색)클릭 이벤트 처리 및 비동기 동작 구현타입 관리:기본 Movie 타입 확장하여 MovieWithFavorite 인터페이스 정의TypeScript 타입 오류 해결을 위한 전략 수립미션 해결 회고로그인 없이 사용자별 데이터를 관리하기 위해 기기 식별자를 활용한 접근법을 알게되어 활용해보았습니다. Supabase로 찜한 정보를 저장하고 관리하는 과정에서 서버리스 데이터베이스의 강력함을 경험했습니다.타입스크립트의 인터페이스 확장을 통해 is_favorite 속성을 추가하며 타입 시스템의 중요성에 대해 또 느꼈습니다.원하는 기능이 있을때마다 supabase에 테이블을 추가하여 활용하는 것이 신기했습니다.

풀스택인프런워밍업풀스택supabase

윤기숙

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

📖 3주차 - 학습 내용 정리1. 피드백 컴포넌트 Alert, Toast, Progress bar, Spinner loader, Skeleton loader, Slot, ModalProgress bar의 길이가 적용되기 위해 Constraints → Scale을 적용Spinner loader를 위해 Ark 조정 및 프로토타입을 통해 적용해보는 것이 좋았다. 2. 네비게이션 컴포넌트 Link, Tabs, Breadcrumbs, Navigation, Pagination, Carousel 3. 베리어블 모드모드가 필요한 때는 언제인가?라이트모드, 다크모드멀티브랜드 지원디바이스별 대응다중언어 지원시다크 모드는 왜 필요할까?- 빛이 덜 발산해서 배터리가 덜 듦, 저시력자의 경우 다크 모드를 선호, 몰입형 미디어 경우.다크모드는 단순히 색상을 반전시키는 것이 아니다. 반전 후 접근성을 반드시 체크해야한다. 기타 베리어블 모드 활용 - 브랜드 모드, 반응형, 멀티 언어 전환하기.반응형 모드를 만들 때, 반드시 오토레이아웃을 만들어야 Min-width, Max-width 설정 가능. 💡 3주차 - 회고 및 느낀 점 😆 좋았던 점나에게 베리어블도 생소한 편이였는데 모드 전환은 진짜 신세계였다. 4가지 모드별로 직접 변환하고 실습할 수 있었던 점이 진짜 도움이 많이 되었다고 생각한다. 지난주까지는 그저 따라가기 바빴는데, 이번 주차 부터는 조금 더 왜 이런것이 필요한지, 어떤 경우에 쓰면 좋은지 이해하면서 진행할 수 있게 된 점이 좋았다. 체계적으로 강의가 진행되면서, 처음에는 익숙하지 않았던 부분들도 3주차되면서는 자연스럽게 적응 되는 그런 학습 진도가 너무 좋았던 것 같다. 😅 아쉬웠던 점분명 순서대로 모든 강의 내용대로 따라갔다고 생각했는데, 모드 전환이나 여러가지를 통해서, 보다보면 빠진 부분들을 발견하게 되었다. 그런 부분들을 다시 돌아가서 추가하고, 변경하고 그런부분들에 생각보다 시간 소요가 많이 있었다. 가끔 모드를 전환시 어떤 부분들은 가끔 변경되지 않는 부분들을 발견한다. 😍 앞으로 바라는 점다음주는 가족 여행 계획이 있어서 얼마나 미션을 할 수 있을지 모르겠는데 끝까지 최선을 다하자. 

UX/UI볼드UX디자인시스템피그마워밍업클럽

cs

[인프런 워밍업 클럽 3기 CS] 운영체제 3주 차 미션

운영체제1. 메모리의 종류와 특징1. 레지스터(Register) – CPU 내부에 위치, 가장 빠름, 용량 작음.2. 캐시(Cache) – CPU와 RAM 사이에 위치, 자주 사용하는 데이터를 저장하여 속도 향상.3. 메인 메모리(RAM) – 휘발성(전원 차단 시 데이터 삭제), 실행 중인 프로그램이 사용.4. 가상 메모리(Virtual Memory) – RAM이 부족할 때 HDD/SSD 일부를 메모리처럼 사용.5. 보조 저장 장치(HDD/SSD) – 비휘발성, 데이터를 영구 저장하는 장치. 2. 사용자 프로세스가 운영체제 메모리 영역에 침범하지 못하도록 만든 레지스터• 베이스 레지스터(Base Register): 프로세스의 메모리 시작 주소 저장.• 한계 레지스터(Limit Register): 프로세스가 접근할 수 있는 최대 범위 설정.• 이 두 개를 사용하여 운영체제 메모리를 보호함. 3. 가변 분할 방식 vs. 고정 분할 방식 가변 분할 방식고정 분할 방식장점메모리를 효율적으로 사용 가능내부 단편화 없음단점외부 단편화 발생 가능고정 크기로 인해 메모리 낭비 발생 4. CPU 사용률이 0%에 가까워지는 현상• 스레싱(Thrashing)• 이유: 멀티프로그래밍을 너무 많이 실행하여 페이지 부재(Page Fault)가 증가하고, 스왑(Swap)이 빈번하게 발생하여 CPU가 거의 사용되지 않음. 5. HDD나 SSD는 컴퓨터 실행에 필수인가?• 필수는 아님.• 이유: 운영체제(OS)가 RAM에서 실행되면 부팅이 가능하므로, HDD/SSD 없이도 실행 가능. 다만, OS를 저장할 공간이 필요하므로 일반적으로 사용됨. 6. 파일을 삭제해도 포렌식으로 복구할 수 있는 이유• 파일을 삭제해도 실제 데이터는 삭제되지 않음.• 운영체제는 단순히 파일의 메타데이터(인덱스)만 삭제하고, 기존 데이터는 그대로 남아 있음.• 새로운 데이터가 덮어쓰기 전까지 복구 가능.

시스템 · 운영체제

cs 14일 전
연유

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

✏ 학습회고 3주차 운영체제 강의에서는 가상메모리, 입출력장치, 파일시스템을 공부했습니다. 알고리즘은 설명만 듣고 구현해보니 훨씬 더 깊이 이해할 수 있었습니다.🎯 미션 운영체제 메모리의 종류는 어떤것들이 있나요? 각 메모리의 특징도 함께 적어주세요.레지스터 : 가장 빠른 기억장치로 CPU내 존재캐시 : 메인 메모리의 값을 레지스터로 옮기려면 한참 걸리므로 필요할 것 같은 데이터를 미리 가져와 저장메인 메모리 : 운영체제와 다른 프로세스들이 올라가는 공간보조저장장치 : 비휘발성 메모리로 데이터를 저장하는 공간 사용자 프로세스가 메모리의 운영체제 영역에 침범하지 못하도록 만든 레지스터는 어떤 레지스터일까요?경계 레지스터.경계 레지스터는 사용자 프로세스가 경계 레지스터 값을 벗어났다면 프로세스를 종료시킨다  메모리 할당 방식에서 가변 분할 방식과 고정 분할 방식의 장단점은 뭔가요?가변 분할 방식은 프로세스가 크면 메모리도 크게 할당하여 내부 단편화가 없지만 외부 단편화가 발생한다.고정 분할 방식은 프로세스의 크기와 상관없이 메모리를 할당하여 구현이 간단하고 오버헤드가 적지만 공간이 낭비되는 내부단편화가 발생한다. CPU 사용률을 올리기 위해 멀티프로그래밍을 올렸지만 스왑이 더 많이 이루어져 CPU 사용률이 0%에 가까워 지는 것을 뭐라고 할까요?스레싱 HDD나 SSD는 컴퓨터를 실행시키는데 꼭 필요한 걸까요?  HDD나 SSD에는 운영체제가 저장되어 있기 때문에 컴퓨터를 실행시키는데 꼭 필요합니다. 파일을 삭제해도 포렌식으로 파일을 복구할 수 있는 이유가 무엇일까요? 파일을 삭제하면 파일시스템은 파일의 모든 정보를 지우는 것이 아니라 파일 테이블에서 헤더를 지우는데, 마치 파일이 삭제된 것처럼 보인다. 하지만 사용했던 블록데이터는 그대로 있기 때문에 복구가 가능하다.  자료구조와 알고리즘 지금까지 배운 5개의 정렬 알고리즘의 장단점과 시간 복잡도를 적어주세요.버블정렬, 선택정렬, 삽입정렬이해하기 쉽고 구현하기도 간단하다성능이 좋지않다시간 복잡도 O(n^2) 병합정렬성능이 좋다이해와 구현이 어렵다시간 복잡도 O(n log n) 퀵정렬 병합정렬보다 적은 비교와 적은 메모리 공간을 차지한다성능이 좋다이해와 구현이 어렵다시간 복잡도 O(n^2), ∂(n log n) 메모리가 부족한 시스템에서 어떤 문제를 해결하는데 재귀로 쉽게 구현이 가능할 것 같습니다. 여러분이라면 메모이제이션과 타뷸레이션 중 어떤 걸 이용하실 건가요? 이유를 함께 적어주세요. 타뷸레이션을 이용하겠습니다. 타뷸레이션은 메모리를 적게 사용하므로 메모리가 부족한 시스템에서 적절합니다. 📝회고 병합정렬과 퀵정렬은 설명이 어려워서 강의를 반복해서 들었습니다.기간 내 강의를 모두 수강하여 완주를 했는데 뿌듯함을 느꼈습니다. 앞으로도 인프런 강의를 통해서 성장하는 개발자가 되고 싶습니다.

tikitaka

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

 [풀스택 완성] Supabase로 웹사이트 3개 클론하기 (Next.js 14) 3주차에는 Netflix 클론 코딩을 진행했다.Supabase를 사용해 영화 테이블을 생성하고 TMDB의 영화 데이터를 삽입했다.또한, react-intersection-observer 패키지를 활용해 무한 스크롤을 구현하고, useInfiniteQuery로 페이지네이션까지 적용했다.영화 개별 페이지에서는 SEO 최적화를 위해 generateMetadata를 사용하여 ogImage까지 설정했다. 무한 스크롤 적용 방식useInfiniteQuery를 활용한 페이지네이션 useInfiniteQuery를 사용하여 검색 결과를 페이지 단위로 가져온다.queryFn에서 searchMovies 함수를 호출하여 검색어search와 페이지 번호pageParam를 기반으로 데이터를 요청한다.getNextPageParam을 이용해 다음 페이지 번호를 설정하여, lastPage.page 값이 존재하면 다음 페이지를 요청하고, 없으면 추가 요청을 중단한다.react-intersection-observer를 활용한 스크롤 감지useInView를 사용하여 특정 요소가 뷰포트 안에 들어오는지를 감지한다.threshold: 0 는 요소가 화면에 보이기 시작하면 inView가 true로 변경ref를 특정 요소에 연결하면, 그 요소가 화면에 보일 때 inView 값이 변한다.useEffect를 이용한 자동 데이터 로드inView 값이 true이고, 다음 페이지가 존재(hasNextPage)하며, 현재 데이터를 요청 중(isFetching, isFetchingNextPage)이 아니라면 fetchNextPage를 호출하여 다음 페이지 데이터를 불러온다. SEO 최적화generateMetadata 함수는 특정 페이지의 메타데이터(SEO 정보)를 동적으로 생성하는 역할을 한다.generateMetadata에서 아래와 같이 반환한다면:return { title: movie.title, description: movie.overview, openGraph: { images: [movie.image_url], }, };HTML 메타태그가 아래와 같이 생성된다:<title>Inception</title> <meta name="description" content="Follow ..." /> <meta property="og:image" content="https://image.tmdb.org/t/abc.jpg" />Open Graph images를 추가하여 SNS에서 공유 시 미리보기 이미지가 표시되도록 한다. 3주차 미션Netflix Clone 프로젝트에 “찜하기” 기능을 추가하세요.사용자가 특정 영화를 “찜”할 수 있도록 Supabase를 활용해 즐겨찾기 리스트 구현찜한 영화를 영화 리스트 화면의 최상단으로 보여주도록 정렬github: https://github.com/thayoon/inflearn-nextjs-supabase-netflix-clone결과 화면:미션 해결 과정:1. 추후 회원 기능 추가를 위해 Supabase에서 “찜(favorites) 테이블”을 생성한다.favorites: 사용자가 찜한 영화를 저장하는 테이블한 영화(movie.id)는 여러 사용자의 favorites에 포함될 수 있다. (1:N 관계)2. 전체 영화 리스트와 찜 상태를 함께 가져오는 RCP 함수 정의Supabase에서 서브쿼리를 사용하여 영화와 찜 상태를 가져오는 get_movies_with_favorites라는 Remote Procedure Call (RPC) 함수를 정의한다.이 함수는 영화 제목을 검색하고, 해당 영화가 찜 목록에 있는지 여부(boolean)를 favorite 컬럼으로 반환한다.JavaScript: Call a Postgres function | Supabase Docsget_movies_with_favorites-- DROP FUNCTION IF EXISTS get_movies_with_favorites(varchar, integer, integer); create or replace function get_movies_with_favorites(search varchar, page int, page_size int) returns table ( id int8, image_url text, title varchar, overview varchar, vote_average float8, popularity float8, release_date varchar, favorite boolean ) as $$ begin return query select movie.*, exists ( select 1 from favorites where favorites.movie_id = movie.id ) as favorite from movie where movie.title like '%' || search || '%' limit page_size offset (page - 1) * page_size; end; $$ language plpgsql;3. 기존 영화 목록 불러오기 로직 변경기존의 movie 테이블에서 select()를 사용해 영화를 불러오는 로직을:export async function searchMovies({ search = "", page, page_size }) { // ... const { data, count, error } = await supabase .from("movie") .select("*") .like("title", `%${search}%`) .range((page - 1) * pageSize, page * pageSize - 1); // ... }위에서 정의한 get_movies_with_favorites RPC를 호출하는 방식으로 변경한다.:export async function searchMovies({ search = "", page, page_size }) { // ... const { data, error } = await supabase.rpc("get_movies_with_favorites", { search, page, page_size, }); // ... }그 결과 찜 상태를 포함한 영화를 반환받을 수 있다.:[ { id: 1, image_url: 'https://image.tmdb.org/t/p/w500/1pdfLvkbY9ohJlCjQH2CZjjYVvJ.jpg', title: 'Dune: Part Two', overview: 'Follow the mythic journey of Paul Atreides as he unites with Chani and the Fremen while on a path of revenge against the conspirators who destroyed his family. Facing a choice between the love of his life and the fate of the known universe, Paul endeavors to prevent a terrible future only he can foresee.', vote_average: 8.3, popularity: 3437.313, release_date: '2024-02-27', favorite: false }, { id: 2, image_url: 'https://image.tmdb.org/t/p/w500/kDp1vUBnMpe8ak4rjgl3cLELqjU.jpg', title: 'Kung Fu Panda 4', overview: 'Po is gearing up to become the spiritual leader of his Valley of Peace, but also needs someone to take his place as Dragon Warrior. As such, he will train a new kung fu practitioner for the spot and will encounter a villain called the Chameleon who conjures villains from the past. movie HD QUALITY, open this link leakedcinema.com', vote_average: 7.146, popularity: 2340.977, release_date: '2024-03-02', favorite: false }, // ... ]4. 영화 카드 컴포넌트에서 찜 상태 반영components/movie-card.tsxexport default function MovieCard({ movie }) { async function handleClick(favorite: boolean) { if (favorite) { await insertFavoriteMutaion.mutate(); } else { await deleteFavoriteMutaion.mutate(); } } return ( <div className="col-span-1 relative"> // ... {/* 찜하기 버튼 */} <div className={`z-30 absolute top-2 right-2 rounded-full bg-gray-900/20 hover:bg-gray-900 transition duration-300`}> <IconButton variant="text" className="rounded-full" onClick={() => { handleClick(!movie.favorite); }} > {insertFavoriteMutaion.isPending || deleteFavoriteMutaion.isPending ? ( <Spinner /> ) : ( <i className={`fas fa-heart text-2xl ${movie.favorite ? "text-red-800" : "text-white"}`} /> )} </IconButton> </div> // ... </div> ); }MovieCard 컴포넌트에서 각 영화의 찜 상태(favorite)에 맞게 버튼 색상을 변경하고, 클릭 시 handleClick 함수로 찜 상태를 변경하고 해당 상태를 mutate를 통해 서버와 동기화한다.4-1. 찜 상태 반영 후 데이터 업데이트const insertFavoriteMutaion = useMutation({ mutationFn: () => insertFavorite(movie.id), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ["favoritesMovieList"], }); queryClient.invalidateQueries({ queryKey: ["movie"], }); }, });insertFavoriteMutaion과 deleteFavoriteMutaion에서 각각 찜 추가 및 삭제가 성공하면 queryClient.invalidateQueries를 사용해 관련된 데이터를 새로고침하여 UI를 최신 상태로 반영하여 유지한다.5. 화면 상단에 보여질 찜한 영화 리스트 불러오기Supabase에서 두 테이블을 외래키 기준으로 조인하여 찜한 영화 목록만 가져온다.Query nested foreign tables through a join table - JavaScript: Fetch data | Supabase Docsactions/movieActions.tsexport async function getFavoritesMovie() { const supabase = await createServerSupabaseClient(); const { data, error } = await supabase .from("favorites") .select(`*, movie!favorites_movie_id_fkey(*)`) .order("created_at", { ascending: false }); if (error) { handleError(error); } return data.map((item) => ({ ...item, movie: { ...item.movie, favorite: true, }, })); }여기서 movie 정보에 favorite 프로퍼티를 추가하고, 찜한 영화 목록이므로 모든 영화에 favorite: true를 설정한다.응답 예시:[ { id: 78, created_at: '2025-03-22T13:19:51.598+00:00', movie_id: 1, movie: { id: 1, title: 'Dune: Part Two', overview: 'Follow the mythic journey of Paul Atreides as he unites with Chani and the Fremen while on a path of revenge against the conspirators who destroyed his family. Facing a choice between the love of his life and the fate of the known universe, Paul endeavors to prevent a terrible future only he can foresee.', image_url: 'https://image.tmdb.org/t/p/w500/1pdfLvkbY9ohJlCjQH2CZjjYVvJ.jpg', popularity: 3437.313, release_date: '2024-02-27', vote_average: 8.3, favorite: true } } ]6. 찜한 목록 Swiper 적용하여 화면 상단에 출력하기components/movie-favorites-list.tsx찜한 영화가 없을 경우, 데이터를 추가할 수 있도록 전체 영화 리스트로 스크롤 이동 버튼을 추가한다."use client"; import { useRef } from "react"; export default function MovieFavoritesList() { // 전체 영화 리스트로 스크롤 이동 const moveRef = useRef(null); const scrollToAllMovies = () => { moveRef.current?.scrollIntoView({ behavior: "smooth", block: "start", }); }; return ( // ... <> <div className="flex justify-center items-center pt-16 pb-8"> <div className="flex flex-col items-center space-y-4"> {getFavoritesMovieQuery.isLoading ? ( <Spinner /> ) : ( <> <p className="text-xl">좋아하는 영화를 추가해보세요.</p> <button onClick={scrollToAllMovies} className="w-12 h-12 bg-gray-900 text-white rounded-full flex items-center justify-center shadow-lg hover:bg-black transition duration-300" > <i className="fa fa-angle-double-down" /> </button> </> )} </div> </div> <div className="p-8" ref={moveRef}></div> </> // ... ) };찜한 영화가 있을 경우, Swiper를 적용하여 캐러셀 형식으로 나타낸다.// ... import { getFavoritesMovie } from "actions/movieActions"; import { Navigation } from "swiper/modules"; import { Swiper, SwiperSlide } from "swiper/react"; import "swiper/css"; import "swiper/css/navigation"; export default function MovieFavoritesList() { // ... const getFavoritesMovieQuery = useQuery({ queryKey: ["favoritesMovieList"], queryFn: () => getFavoritesMovie(), }); return ( <div className="w-full"> <h1 className="px-2 text-2xl font-bold">Your Favorite Movies💘</h1> {getFavoritesMovieQuery.isLoading || !getFavoritesMovieQuery?.data.length ? ( // 데이터가 없는 경우 ) : ( <Swiper modules={[Navigation]} spaceBetween={5} slidesPerView={4} breakpoints={{ 0: { slidesPerView: 1 }, // 반응형 적용 320: { slidesPerView: 2 }, 540: { slidesPerView: 3 }, 720: { slidesPerView: 4 }, 1024: { slidesPerView: 5 }, }} navigation={{ nextEl: ".custom-swiper-button-next", // 커스텀 스타일 적용 prevEl: ".custom-swiper-button-prev", }} className="relative" > <button className="custom-swiper-button-prev absolute left-2 top-1/2 transform -translate-y-1/2 w-10 h-10 bg-black/50 text-white rounded-full flex items-center justify-center hover:bg-black/80 disabled:opacity-0 disabled:cursor-auto transition duration-300 z-10"> ◀ </button> <button className="custom-swiper-button-next absolute right-2 top-1/2 transform -translate-y-1/2 w-10 h-10 bg-black/50 text-white rounded-full flex items-center justify-center hover:bg-black/80 disabled:opacity-0 disabled:cursor-auto transition duration-300 z-10"> ▶ </button> {getFavoritesMovieQuery.data.map((item) => ( <SwiperSlide key={item.id}> <MovieCard movie={item.movie} /> </SwiperSlide> ))} </Swiper> )} </div> ); } 3주차 회고이번 주 미션을 진행하면서, 처음에는 영화 목록을 가져올 때 페이지네이션을 고려하지 않고 RPC를 사용하여 데이터를 가져왔다. 하지만 구현하면서 이 방식이 최선일까? 라는 고민이 들었다.생각해본 접근 방식으로는, 처음 데이터를 가져온 후 가공하여 Recoil로 상태 관리하는 방법이 있었다.이렇게 하면 불필요한 데이터 호출을 줄일 수 있을 것 같다.하지만 이번 과제를 통해 레퍼런스를 참고하며 다양한 SQL 쿼리문을 활용하는 경험을 쌓을 수 있었던 점은 큰 배움이었다.다음주까지 모두 학습하고 사용자 인증을 적용한 후 사용자별 찜 기능, 상세 페이지에서 찜 추가/삭제 기능까지 구현할 계획이다.다음 주가 마지막 주라 강의가 많아 최대한 빠르게 수강하고 실습해야 하지만, Supabase의 하이라이트 부분을 배우게 되어 기대가 크다!

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

이 글은 박우빈님의 Practical-Testing 강의 를 참조하여 작성한 글입니다.어느덧 수료까지 일주일만을 앞두고 있다. 벌써 75%나 진행되었다니,,,이때까지 강의 안 밀리고 시간 안에 미션 제출한 내 자신 칭찬한다👍🏻 (아직 강의 섹션3개, 미션 2개 남음)남은 한 주도 열심히 해서 수료해야지!! 이번 주는 테스트 코드가 필요한 이유 및 레이어드 아키텍쳐 내에서 각 레이어드별 테스트 코드 작성하는 법에 대해 배웠다.강의를 듣고 테스트 코드를 작성해야 하는 이유에 대해서 완전 설득이 되었다.기존에는 테스트 코드 작성하는 것이 너무 귀찮고 시간이 오래 걸린다는 이유에서 꺼려했지만, 점점 리팩토링 또는 기능을 추가할 때마다 수동으로 테스트 하는게 더욱 귀찮았다. 또한 수동으로 테스트를 했어도 항상 찝찝함이 존재했다.그래서 우빈님 강의를 들으면서 엄청난 공감을 느꼈고, 강의를 열심히 학습해서,,,많이 배워야겠다는 다짐을 다시 하게된 것 같다.다음으로 readable-code 강의에서 만든 스터디 카페 이용권 프로그램에 대해 스스로 테스트 코드를 작성해 보는 시간을 가졌다.이번에는 열심히 작성한 후 리뷰 신청도 하였다.테스트 코드 관련해서는 리뷰를 한번도 받아본 적이 없기도 하고, 코드 작성 중 궁금한 점이 생겨 리뷰 신청을 하는 용기를 내보았다..ㅋㅋㅋ다음 주 중간점검 때 리뷰를 해주실 예정인데 기대된다!!!학습 내용 요약 테스트는 왜 필요할까?빠른 피드백리팩토링, 신규 기능 추가 등 변화가 생기는 매순간마다 테스트 코드를 통해, 기존 코드가 정상 동작하는 지 빠르게 피드백을 받을 수 있다.만약 테스트 코드를 작성하지 않는다면?변화가 생기는 매순간마다 발생할 수 있는 모든 Case를 고려해야 한다.변화가 생기는 매순간마다 모든 팀원이 동일한 고민을 해야 한다.자동화자동화 테스트로 비교적 빠른 시간 안에 버그를 발견할 수 있고, 수동 테스트에 드는 비용을 크게 절약할 수 있다.(인간이 수동으로 테스트를 하게 된다면 실수할 확률이 매우 높음)안정성빠르게 변화하는 소프트웨어에서 테스트 코드를 통해 100%는 아니지만 안정성을 보장할 수 있다. 단위테스트단위테스트란?작은 코드 단위를 독립적으로 검증하는 테스트통합테스트에 비해 준비해야 할 코드가 적으며, 검증 속도가 빠르고 안정적이다.단위 테스트만으로는 기능 전체의 신뢰성을 보장할 수 없다는 단점이 존재한다.JUnit5: 단위 테스트를 위한 테스트 프레임 워크AssertJ: 테스트 코드 작성을 원활하게 돕는 테스트 라이브러리 (풍부한 API, 메서드 체이닝 지원) TDD: Test Driven Development선 기능 구현, 후 테스트 작성테스트 누락 가능성 존재해피 케이스만 검증할 가능성 존재잘못된 구현을 늦게 발견할 가능성 존재선 테스트 작성, 후 기능 구현 (TDD)복잡도가 낮은, 테스트 가능한 코드로 구현할 수 있게 한다(유연하며 유지보수 쉬운 코드)쉽게 발견하기 어려운 엣지 케이스를 놓치지 않게 해줌구현에 대한 빠른 피드백을 받을 수 있음 -> 과감한 리팩토링 가능해짐 Spring / JPA 훑어보기 & 기본 엔티티 설계라이브러리내 코드가 주체가 돼서 필요한 기능이 있다면 외부에서 끌어와서 사용하게 되는데, 이 때 외부에 있는 것들을 라이브러리라 한다.따라서 라이브러리는 내 코드가 주체가 되는 환경이고 능동적인 특징을 지닌다.프레임워크이미 갖춰진 동작할 수 있는 그런 환경들이 구성이 돼 있고, 내 코드가 이 프레임 안에 들어가는 수동적인 역할을 한다.따라서 프레임워크에서는 내 코드가 수동적인 존재가 된다.ORM 객체 지향 프로그래밍(OOP)과 관계형 데이터베이스(RDB) 간의 구조적 차이를 해소하기 위해 사용되는 기술이다.데이터베이스에 CRUD하는 작업을 객체 기반으로 처리함으로써 개발자들이 기본적인 쿼리 작성하는 단순 작업을 줄이고 비지니스 로직에 집중할 수 있게 해준다.JPAJava진영의 ORM 기술 표준인터페이스이며, 여러 구현체가 있지만 주로 Hibernate를 많이 사용한다. Spring 진영에서는 JPA를 한번 더 추상화한 Spring Data JPA제공한다. Persistence Layer 테스트데이터에 접근하는 역할로 데이터 CRUD와 연관된 메서드들이 위치해 있다.비지니스 로직이 포함돼서는 안됨!@DataJpaTestJPA와 관련된 의존성들만 주입해준다 -> @SpringBootTest보다 가볍다.어노테이션 내부에 @Transactional이 포함되어 있어 테스트 후 데이터가 롤백된다. Business Layer 테스트비지니스 로직에 관련된 메서드들이 위치해 있다.@Transactional vs sql을 이용한 데이터 삭제실제 코드에서는 @Transactional을 사용하지 않는데, 단순히 롤백만을 위해 테스트코드에서 @Transactional을 사용하면, 실제 작동 방식과 다르게 작동할 수 있다.따라서 테스트 코드 작성시 Transactional의 부작용에 대해 인지하고 사용할 것!!리포지토리 테스트와 로직이 많이 없는 얇은 서비스 테스트는 결이 비슷하다왜냐하면 리포지토리에 대한 결이 그대로 오기 때문이다.하지만서비스가 더 기능이 추가될수록 발전을 하기 때문에, 동일한 테스트라고 생각이 되더라도 작성을 하는 게 좋다! (검증이 추가될 수도 있기 때문)클래스 상단에 @Transactional(readOnly = true) 로 표현하고 실제 트랜잭션이 사용되는 메서드에 @Transactional로 표현해주기! 미션Day 11 - 스터디 카페 이용권 선택 시스템 테스트 코드 작성하기나의 코드: https://github.com/Jiihyun/readable-code/pull/3🤔 고민사항 1. StudyCafeSeatPassTest 클래스테스트 케이스 반복을 줄이기 위해 @CsvSource를 사용해 보았다.이로 인해 데이터만 추가하면되니 테스트 케이스를 쉽게 확장할 수 있다는 장점을 느꼈다.하지만 StudyCafePassType을 직접 문자열로 작성했다보니, 후에 passType이 수정될 경우 테스트 코드 내 passType을 직접 수정해야 하기 때문에 유지보수 비용이 증가할 것 같다는 생각이 들기도 했다..그래서 현업에서는 어떤 방식으로 테스트를 하지? 하는 궁금증이 생겼던 것 같다.두 방법에 대해 트레이드오프가 존재하는데 어떤 걸 더 중요시하게 여겨 테스트하는지 궁금하다!2. FileReaderTest 클래스파일을 잘 읽어와 데이터를 의도한 대로 파싱하였는지 테스트하고 싶었다.하지만 반환 타입이 일급컬렉션으로 되어 있고,컬렉션 내의 데이터를 확인하는 메서드는 프로그램에서 사용되지 않기 때문에 존재하지 않았다.테스트를 공부하면서 배운 것 중 하나는 테스트만을 위한 메서드는 최대한 자제해야 한다고 했다.그래서 오로지 테스트를 위해 컬렉션의 크기 등을 확인할 수 있는 메서드를 추가하고 싶지 않았고, 그래서 일급 컬렉션의 메서드를 직접 호출하여 테스트를 진행했다.그치만 이렇게 짜여진 테스트도 본 적이 없는데....이런 방식으로 테스트를 해도 괜찮은지 궁금하다 ㅋㅎㅎㅋ마무리앞으로 이제 일주일 남았다..!! 벌써...?다음 주 학습 내용에는 평소에 궁금했던 내용들에 대해 다루기 때문에 매우 기대가 된다.배워본 적 없는 내용이라 시간을 많이 투자해야 할 것 하지만,,,열심히 학습해서 내 것으로 만들어야지!

백엔드클린코드테스트코드발자국워밍업스터디클럽

이수진

[인프런 워밍업 클럽 Full-Stack 3기] 3주차 발자국 - Netflix 클론코딩

이번주는 Netflix 클론코딩에 관한 내용이었다. Netflix 클론코딩이라던지 무한스크롤 구현이라던지 하는 내용은 이전에도 다른 강의에서 자주 나왔던 항목이어서 그런지 들을 때 좀 가볍게 들었던 것 같다. 다음 주에 있는 인스타그램 클론코딩의 강의+과제 볼륨이 거의 2주치가 되어서 이번주는 쉬어가는 듯 가볍게 듣고 다음 챕터 강의에 집중하려했다.이번 강의의 핵심은 supabase table에서 데이터 가져오기, 무한 스크롤 구현, SEO 최적화하기였다. 수강 내용Section 5. Netflix 클론코딩 - 영화검색 서비스 제작하기이번 챕터에선 Netflix 클론코딩을했다. 어느 때와 마찬가지로 tmdb에서 데이터를 가져와 영화를 뿌려주고, 해당 영화에 대한 정보를 보여주는 것이었다. 기술 스택은 지난번과 마찬가지로 Next.js, tailwind css를 기반으로했고 이번 프로젝트에서는 특히 상태관리가 필요해 zustand 라이브러리를 선택했다. 강의에서는 recoil을 사용했지만 리코일의경우 Next.js 15버전과 호환이 잘 되지 않기도 하고 평소에 가벼운 zustand를 자주 사용해서 해당 라이브러리로 상태관리를 진행했다.영화 목록 전체 불러오기우선 영화 데이터는 강의에서 준비해서 supabase 테이블에 모두 넣고 해당 테이블의 데이터들을 모두 불러오는 코드를 작성했다. const { data, error } = await supabase .from("movie") .select("*")검색 기능 추가하기위에서 말한 상태관리 라이브러리는 이 검색 기능을 활용하기 위해 사용한다. Header 컴포넌트에 있는 SearchInput 에서 검색을 하면 다른 곳에서도 해당 검색어를 사용하기 위해 검색어를 전역 변수로 지정했다.const { data, error } = await supabase .from("movie") .select("*") .like("title", `%${search}%`)search 키워드를 받아와 supabase table 내 title 컬럼에서 search 키워드가 포함된 항목들을 검색한다. 보면서 느끼는건데 확실히 SQL을 알고있으면 이런 키워드를 이해하는 데 좀 더 쉬운거같단 생각이 든다. table이라 그런지 다 SQL문법 사용하네.. 아무튼 이렇게 하면 supabase table에서도 검색어를 손쉽게 찾을 수 있다.무한 스크롤 구현무한 스크롤 구현하는 방법은 매우 다양하다. intersection observer을 사용한다던지.. 이번 강의에서는 가볍게 tanstack-query와 react-intersection-observer 라이브러리를 통해 무한 스크롤을 구현했다.const { ref, inView } = useInView({ threshold: 0 });위는 react-intersection-observer에서 사용하는 hooks이다.ref : 참조할 요소를 지정한다. inView : 요소를 불러와야 할 경우를 true false로 판별한다.threshold : 얼만큼 겹쳤을 경우 inView를 변경할 지 설정한다.이를 통해 데이터를 불러오는 곳 하단에 <div ref={ref} /> 을 작성하면 하단에 닿았을 경우의 트리거가 완성된다. 그리고 그 다음에는 tanstack-query에 있는 useInfiniteQuery를 사용한다.const { data, isFetchingNextPage, isFetching, hasNextPage, fetchNextPage } = useInfiniteQuery({ initialPageParam: 1, queryKey: ["movie", keyword], queryFn: ({ pageParam }) => searchMovies({ search: keyword, page: pageParam, pageSize: 12 }), getNextPageParam: (lastPage, allPages) => { return lastPage.page ? lastPage.page + 1 : undefined; }, });대충 이런식으로.. useQuery랑은 비슷하지만 hasNextPage, fetNextPage등 무한 스크롤 구현에 유용한 기능들이 포함되어있다. 강의를 들으면 깔끔하게 무한 스크롤까지 구현이 가능해진다.SEO (Next.js generateMetadata)Next.js에서는 동적으로 metadata를 생성해주는 기능을 제공한다. dynamic page같은 경우 각 페이지별로 메타데이터를 설정해주려고 하면 예를들어 id를 1, 2, 3 이렇게 다 따로 만들 수 없으니 이 때 generateMetadata를 사용하면 된다.export async function generateMetadata({ params }: any) { // Next.js에서는 params를 await 해야 함 const { id } = await params; const movie = await getMovie(Number(id)); return { title: movie?.title, description: movie?.overview, openGraph: { images: [movie?.image_url], }, }; }강의에서는 Next.js 14 버전이라 별 문제가 없었지만 나는 Next.js 15 버전을 사용해서 params를 가져올 때 async-await을 사용해서 가져왔다. 15버전에서 그냥 가져오면 동작은 하지만 에러가 발생한다. 이렇게까지 하면 가볍게 Netflix 클론코딩은 클리어. 미션3주차미션은 "찜하기" 기능을 구현하는 것이다. 유저 정보도 아직 없고 어떻게 구현할까 하다가 역시 로컬에 저장하는건 localstorage가 답이다 생각했다. 하지만 Next.js 는 SSR이라 localstorage를 그저 React처럼 사용한다면 기능이 정상적으로 동작하지 않는다. 따라서 zustand의 persist를 통해 로컬스토리지에 데이터를 쉽게 사용할 수 있도록 구현했다.마무리 이번주도 주말까지 무사히 일정을 잘 맞췄다. 몇번 했던 기술들이었지만 한번 더 복습 겸 꼼꼼히 들었다. 예전에는 javascript api 중 intersection observer을 이용해 무한스크롤을 깡으로 구현했었는데 확실히 라이브러리를 통해 구현하니까 많이 간편했다. 다음 인스타 클론코딩은 거의 2주치 분량이던데 다음주는 진짜 미리미리 듣고 추가미션까지 해낼 수 있도록 노력해야겠다. 마지막 4주차도 화이팅!

웹 개발웹개발프론트엔드풀스택supabase

워밍업 클럽 3기 BE 클린코드 (3주차 발자국) - 테스트코드 작성했으면 됐지..

테스트코드 작성했으면 됐지..기존에도 테스트코드를 많이 짜왔었다. 하지만 이전에 작성했던 테스트코드는 주먹구구식이었음을 이번에 아주 크게 깨닫게 되었다. 어떻게든 테스트코드를 빨리 작성해야겠다는 압박감 때문이었나.. 테스트코드가 깨지면 바로 GPT에게 물어봐서 해결하기 일쑤였다.그중 가장 크게 충격을 먹었었던 부분은 테스트코드의 @Transactional이 끼치는 영향. 이 부분이었다. 실제 프로덕션 코드에는 @Transactional이 붙어있지 않고, 만일 테스트코드에 롤백의 편의성만을 위해 @Transactional을 붙인다면 테스트 환경이 완전히 불일치 해버리는 상황이 나올 수도 있었겠다라는 생각이 들면서 과연 내가 테스트를 잘 작성하고 있었을까? 하는 의문이 들었다. 어떻게든 테스트코드의 초록불을 보기 위해 노력했지만, 여태까지 나는 실제 프로덕션에 나가는 코드의 실제 동작을 고려하지 않고 테스트 코드를 작성했다는 점을 크게 반성했다.강의에서 배웠던 점아무래도 이번 강의에서 배웠던 점들은 사실 이론적인 것 (Presentation & Business & Persistence layer 층별 테스트 방법)은 기존에 좀 사용했었던 터라, 그것 보다는 라이브코딩을 직접 보고 따라 치면서, 실제 프로덕션 코드를 만들고 테스트하는 그 과정에 대해 많이 집중했었다. TDD를 적용해 나가는 그 방식에 대해 많이 배우고 체득할 수 있었다. 또한 앞서 말했던 것처럼 테스트코드의 @Transactional의 신중한 사용..!! 이부분이 정말정말 큰 배움이었다. 

채채

[인프런 워밍업 클럽 스터디 3기] CS - 3주차 미션

운영체제1. 메모리의 종류는 어떤것들이 있나요? 각 메모리의 특징도 함께 적어주세요.컴퓨터에서 사용하는 메모리는 레지스터, 캐시, 메인메모리(RAM), 보조저장장치(HDD, SSD) 등이 있다.- 레지스터는 CPU 내에 있는 가장 빠른 휘발성 메모리로, 비싼 가격과 작은 용량을 가지고 있다.- 캐시는 미리 RAM에서 자주 사용하는 데이터를 가져와서 한다. 레지스터는 빠르나 RAM은 느리기 때문에 캐시에 데이터를 저장한다.- RAM은 비싼 가격으로 인해 실행 중인 프로그램만 올라가는 공간으로 휘발성 메모리이다.- 보조 저장장치는 용량이 크고 가격이 저렴하며 비휘발성 메모리이다. 사용자 프로세스가 메모리의 운영체제 영역에 침범하지 못하도록 만든 레지스터는 어떤 레지스터일까요?경계 레지스터이다.경계 레지스터는 CPU 내에 위치하며, 하드웨어적으로 운영체제 영역과 사용자 영역을 분리한다. 메모리 관리자는 사용자 프로세스가 경계값을 벗어났는 지 감시하고, 벗어났을 경우 그 프로세스를 종료한다. 메모리 할당 방식에서 가변 분할 방식과 고정 분할 방식의 장단점은 뭔가요?가변 분할 방식의 장점은 프로세스 크기에 따라 메모리를 가변적으로 분할할 수 있어 내부 단편화가 발생하는 것이 적다. 단점은 메모리 공간보다 프로세스의 크기가 더 커서 외부 단편화가 발생할 수 있다.고정 분할 방식의 장점은 메모리 할당 시 정해진 크기로 분할하기 때문에 관리와 구현이 간단하다. 단점은 프로세스 크기보다 큰 공간이 할당되면서 내부 단편화가 발생할 수 있다는 것이다. CPU 사용률을 올리기 위해 멀티프로그래밍을 올렸지만 스왑이 더 많이 이루어져 CPU 사용률이 0%에 가까워 지는 것을 뭐라고 할까요?CPU 사용률을 높이려 했지만 잦은 스왑으로 인해 CPU 사용율이 오히려 더 떨어지는 현상을 스레싱이라고 한다. HDD나 SSD는 컴퓨터를 실행시키는데 꼭 필요한 걸까요? 이유를 함께 적어주세요.HHD와 SSD는 컴퓨터를 실행시키는 데 반드시 필요하다. 이들은 운영체제와 프로그램을 저장하기 때문에 없을 경우 부팅에 실패하거나 소프트웨어를 실행할 수 없다. 파일을 삭제해도 포렌식으로 파일을 복구할 수 있는 이유가 무엇일까요?파일을 삭제하면 파일 테이블에서 해당 파일의 헤더만 삭제되고, 삭제된 파일 자체는 free block list에 추가된다. 사용자는 파일 삭제 시 완전히 삭제된 것처럼 느끼나 실제로는 새 데이터로 덮어 쓰여질 때 까지 디스크에 남아있어 포렌식으로 파일을 복구할 수 있다. 알고리즘 & 자료구조지금까지 배운 5개의 정렬 알고리즘의 장단점과 시간 복잡도를 적어주세요.버블 정렬 - O(n), 최악 O(n²)구현하기 쉬우나 이중 for문으로 인해 성능이 좋지 않다.선택 정렬 - O(n²)구현하기 쉬우나 데이터가 많아질수록 비효율적이다.퀵 정렬 - O(NlogN), 최악 O(n²)병합 정렬보다 더 적은 비교를 하고 더 적은 메모리 공간 차지하지만 피벗 선택이 잘못되었을 경우 최악의 시간 복잡도를 가질 수 있다.병합 정렬 - O(NlogN)분할 정복 방식을 적용했다는 장점이 있지만 구현이 복잡하다.삽입 정렬 - O(n²)구현하기 쉽고, 이미 정렬되어있을 경우 효과적이다. 그러나 배열 크기가 커질수록 성능이 저하된다. 메모리가 부족한 시스템에서 어떤 문제를 해결하는데 재귀로 쉽게 구현이 가능할 것 같습니다. 여러분이라면 메모이제이션과 타뷸레이션 중 어떤 걸 이용하실 건가요? 이유를 함께 적어주세요.타뷸레이션 방식을 사용할 것이다.타뷸레이션은 도출된 모든 중간 값을 테이블에 저장하지만, 일반 재귀나 메모이제이션과 비교했을 때 성능이 더 우수하다. 또한, 반복문으로 한 번에 계산을 처리할 수 있기 때문에 메모리가 부족한 시스템에서 더 적합할 수 있다.

kamser

Readable-code 학습 회고 3주차

Readable-code 학습 회고목차1. 강의 수강일주일간 강의 요약 정리일주일간 강의 회고다음 주에 학습목표2. 미션미션을 해결하는 과정어떤 관점에서 접근했는지문제를 해결하는 과정왜 그런 식으로 해결했는지미션 해결에 대한 간단한 회고강의 수강강의 요약TDD레드 - 그린 - 리팩토링이라는 방식으로 매번 순회하여 코드를 작성하는 방법입니다.TDD 장점클라이언트가 필요한 정보만 전달한다고 생각하며 테스트를 작성할 수 있습니다.빠르게 이미 테스트 코드가 작성되어있어 상호 작용으로 메서드와 테스트 코드가 서로 윈윈하게 됩니다.DisplayName1. @DisplayName("음료 1개 추가 테스트") 2. @DisplayName("음료 1개를 추가하면 주문 목록에 담긴다.")아무것도 모르는 상태에서 테스트 코드에 추가된 정보를 보고 확인할 수 있어야합니다.Persistence layer 역할데이터 접근의 역할이 있습니다.비즈니스 가공 로직이 포함되어서는 안됩니다. 데이터를 수정, 삭제, 추가하는 것에 집중합니다.DAO, SQLMapper, QueryDSL을 사용하면 비즈니스 로직 작성이 침투할 수 있습니다.데이터 접근 계층은 데이터에 접근하는 역할만 가져야합니다. 테스트도 마찬가지로 CRUD에 집중하는 레이어 계층이 되어야합니다.Business layer 역할비즈니스 로직을 구현하는 역할입니다.데이터 접근으로 읽어온 데이터를 가공(비즈니스 프로세스)을 하는 계층트랜잭션을 보장해야한다.비즈니스 계층은 기능을 구현하는 로직이 들어옵니다.가장 중요한 역할로 트랜잭션을 보장하는 것입니다.예외가 발생하면 롤백을 할 수 있도록 작업 단위의 원자성을 가지고 있습니다.테스트는 언제나 테스트를 작성하는 목적이 있어야합니다.강의 회고이미 테스트 코드 강의는 모두 본 상태였습니다.그래서 복습한다고 생각하면서 보는 중에 readable-code에서 말씀하신 부분이 나왔습니다.테스트 코드는 내가 작성한 코드가 예상한 결과대로 동작하는지 확인하기 위해서 작성됩니다.그리고 다른 개발자가 내 테스트 코드를 보고 어떤 목적으로 테스트 코드를 작성했는지 가독성이 높여야 한다고 생각이 들었습니다.테스트 코드에서 DisplayName은 객체지향 프로그래밍의 메서도 명과 동일하다고 생각이 됩니다.그것을 보는 개발자는 테스트를 실행하여 테스트 결과 목록을 보면서 추상화된 이름을 보고 어떤 목적으로 테스트를 했는지 확인할 수 있게 작성해야한다고 생각이 변했습니다.기존에 테스트 코드 강의만 봤을 때에는 도메인? 실패/성공이 키워드로 들어가면 안된다 라고 말씀해주신게 이해가 되지 않았습니다.테스트 코드가 결국 누군가에게 보여지는 문서로 활용될 수 있으려면 DisplayName만 보더라도 테스트의 목적을 알 수 있어야하고given when then으로 문맥을 개행으로 나누며, 어떤 것을 테스트 하는지 사고의 흐름에 맞춰 추상적인 흐름이 보이도록 작성해야된다고 생각이 들었습니다.예를 들어 단순히 테스트 준비를 Order.create(모든 파라미터)로 테스트를 준비하면 다른 개발자가 보기에 여기서 중요하게 봐야하는 필드가 무엇인지 한 눈에 들오지 않습니다.이 부분을 우리가 확인하려는 필드만 파라미터로 보여준다면 this.createOreder('모델명','이름','가격')만 보더라도 테스트에서 어떤 필드가 사용되는 구나라고 한눈에 보이게 됩니다.미션Day 11테스트 작성하게된 목적을 먼저 나열해보겠습니다.InputHandler테스트 코드 작성 목적 : 사용자의 요청에 따라 어떤 결과가 나와야하는지 올바르게 확인되어야 뒤에 있는 로직이 신뢰하고 실행될 수 있기 때문입니다.@Test @DisplayName("사용자는 콘솔에 1을 입력하면 시간 이용권이 선택된다.") void selectByStringOne() {  // given  final String userInputAction = "1";  InputHandler inputHandler = createInputHandlerFor(userInputAction); ​  // when  StudyCafePassType userActionCafePassType = inputHandler.getPassTypeSelectingUserAction(); ​  // then  Assertions.assertThat(userActionCafePassType).isEqualTo(StudyCafePassType.HOURLY); }StudyCafePassOrder테스트 코드 작성 목적 : 사용자가 선택한 결과에 따라 올바르게 결과가 출력되는지 확인되어야합니다. 실제 비즈니스 로직에서 사용자의 요청과 비용이 다를 경우에는 상상하기 싫습니다.@Test @DisplayName("할인이 없고, 고정 이용권과 사물함 이용권을 선택하면 두 이용권의 가격을 합산한 가격이 나온다.") void calculatePrice() {  // given  final StudyCafePassType userActionPassType = StudyCafePassType.FIXED;  final double discountRate = 0.0;  StudyCafeSeatPass studyCafeSeatPass = createSeatPassFrom(userActionPassType, 200, discountRate);  StudyCafeLockerPass lockerPass = createLockerPassFrom(userActionPassType, 100);  StudyCafePassOrder passOrder = StudyCafePassOrder.of(studyCafeSeatPass, lockerPass); ​  // when  int price = passOrder.getTotalPrice(); ​  // then  Assertions.assertThat(price).isEqualTo(300); } ​StudyCafeSeatPass테스트 코드 작성 목적 : 이벤트에 따라 할인된 결과가 예상한 결과와 동일해야하는 것을 매번 확인해야합니다. 실제 돈 거래로 예상한 금액과 다를 경우 머리가 복잡해지는 것을 경험할 수 있습니다.@Test @DisplayName("할인율이 1이면 할인되는 금액은 이용권 금액과 같다") void calculatePrice() {  // given  final StudyCafePassType userActionPassType = StudyCafePassType.FIXED;  final double discountRate = 1;  int passPrice = 100;  StudyCafeSeatPass studyCafeSeatPass = StudyCafeSeatPass.of(userActionPassType, 30, passPrice, discountRate); ​  // when  int discountPrice = studyCafeSeatPass.getDiscountPrice(); ​  // then  Assertions.assertThat(discountPrice).isEqualTo(passPrice); }미션 회고강사님이 OT때 말씀하신 내용이 기억납니다.AI에게 도움을 받을 수 있지만 판단력과 지혜와 같이 사람이 결정해야하는 부분에서 능력을 기르는 것이 중요하다.테스트 코드도 마찬가지라고 생각이듭니다단순한 테스트 코드 작성은 AI가 손 쉽게 작성하게 해줍니다.다만 이 테스트 코드를 목적에 맞게 AI가 작성했는지, 테스트 코드를 작성하는 목적이 올바른지 확인해야하는 것은 사람의 몫이라고 생각이 들었습니다.그리고 추가로 중간평가에 알려주신 다른 사람들과 코드를 비교해보라고 말씀해주신게 정말 도움이 많이 되었습니다.20줄 남짓한 코드 리팩토링이 사람마다 다르고 어떤 목적으로 리팩토링을 했는지 확인할 수 있는 부분이 많았습니다.그러면서 제가 놓치고 있던 부분도 확인할 수 있었습니다.총 3개의 코드를 비교해보았습니다.매직 스트링예외 상황에 대한 try-catch검증을 메소드 체이닝으로 처리이렇게 다양한 방식으로 검증을 하는 것으로 제 20줄의 코드가 처음과 많이 달라져있었고그 사이에 다른 개발자분들이 작성한 코드를 보면서 다른 인사이트도 많이 얻었습니다.출처강의 링크Readable Code: 읽기 좋은 코드를 작성하는 사고법Practical Testing: 실용적인 테스트 가이드

김태영

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

3주차 [섹션 6]레이어드 아키텍처(Layered Architecture)와 테스트Presentation Layer, Business Layer, Persistence Layer로 분리관심사의 분리통합 테스트여러 모듈이 협력하는 기능을 통합적으로 검증하는 테스트일반적으로 작은 범위의 단위 테스트만으로는 기능 전체의 신뢰성을 보장할 수 없다.풍부한 단위 테스트 & 큰 기능 단위를 검증하는 통합 테스트Persistene Layer 테스트@SpringBootTestspring boot의 모든 빈들을 다 주입한다.@DataJpaTestJPA관련 빈들만 주입한다.속도가 @SpringBootTest보다 빠르다.Business Layer 테스트비즈니스 로직을 구현하는 부분Persistence Layer와의 상호작용을 통해 비즈니스 로직을 전개시킨다.트랜잭션을 보장해야 한다.@DataJpaTest는 @SpringBootTest와 다르게 @Transactional이 자동으로 걸려있다.데이터 롤백이 기본적으로 된다.@SpringBootTest에 @Tracnsactional을 걸었을 때 주의할 점서비스 클래스에서 @Transactional이 없어도 Test시에 걸어줬기 때문에 테스트가 통과될 수 있다.@AfterEach를 통해서 repository들을 초기화할 수 있다. @Transactional을사용하면 deleteAll 등 초기화 단계를 설정하지 않아도 된다. deleteAll과 deleteAllInBatch의 차이deleteAll은 내부적으로 한 건씩 개별 삭제한다. 연관 관계 테이블을 먼저 지우지 않아도, 개별 삭제할 때 같이 지운다.deleteAllInBatch는 개별 삭제가 아닌 bulk query를 사용하여 한 번에 삭제한다.단, 연관 관계 테이블을 먼저 삭제해야지, 외래키 제약 조건에 어긋나지 않는다.  Presentation Layer 테스트동시성 이슈생성 함수에서는 동시성을 고려해줘야 한다.함수 호출 빈도가 낮으면, id에 unique 제약 조건을 걸고, 실패 시 재시도하는 로직 추가함수 호출 빈도가 높으면, UUID 같은 것으로 대체서비스단에 readOnly = true를 base로 사용cud 작업일 때는 @Transactioal을 메서드 위에 선언해서, readOnly가 되지 않게끔 작동CQRS - command / read를 분리read 부분은 조회 전문 repository나 로직을 구현 가능command는 복잡한 로직을 담당할 수 있다.Day 11 미션  https://github.com/taeyeongKims/readable-code/tree/main/src/test/java/cleancode/studycafe/tobe/model출처 : https://www.inflearn.com/course/practical-testing-%EC%8B%A4%EC%9A%A9%EC%A0%81%EC%9D%B8-%ED%85%8C%EC%8A%A4%ED%8A%B8-%EA%B0%80%EC%9D%B4%EB%93%9C/dashboard

cleancode

appti

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

1. 메모리의 종류는 어떤것들이 있나요? 각 메모리의 특징도 함께 적어주세요.레지스터가장 빠른 기억 장소입니다.CPU 내에 위치합니다.휘발성 메모리입니다.캐시레지스터와 메인 메모리 사이에 위치해 있습니다.매우 빠른 레지스터와 매우 느린 메인 메모리 사이의 속도를 절충하기 위한 목적을 가지고 있습니다.메인 메모리운영체제와 프로세스가 적재되는 메모리입니다.휘발성 메모리입니다.보조저장장치비휘발성 메모리입니다.가격이 저렴합니다.2. 사용자 프로세스가 메모리의 운영체제 영역에 침범하지 못하도록 만든 레지스터는 어떤 레지스터일까요?경계 레지스터3. 메모리 할당 방식에서 가변 분할 방식과 고정 분할 방식의 장단점은 뭔가요?가변 분할 방식내부 단편화가 발생하지 않습니다.외부 단편화가 발생합니다.고정 분할 방식외부 단편화가 발생하지 않습니다.내부 단편화가 발생합니다.4. CPU 사용률을 올리기 위해 멀티프로그래밍을 올렸지만 스왑이 더 많이 이루어져 CPU 사용률이 0%에 가까워 지는 것을 뭐라고 할까요?스레싱5. HDD나 SSD는 컴퓨터를 실행시키는데 꼭 필요한 걸까요? 이유를 함께 적어주세요.폰 노이만 구조는 CPU와 메모리가 필수 사항이기 때문에 HDD나 SSD같은 보조저장장치는 컴퓨터를 실행시키는데 반드시 필요한 것은 아닙니다.다만 컴퓨터 부팅 과정 중 하드디스크에 있는 마스터 부트 레코드에 저장된 부트로더를 메모리로 가져와서 실행하며, 운영체제 또한 대부분 HDD나 SSD에 저장되므로 폰 노이만 구조로만 따진다면 반드시 필요하지는 않지만 사실상 필수적이라고 생각합니다.6. 파일을 삭제해도 포렌식으로 파일을 복구할 수 있는 이유가 무엇일까요?파일 시스템은 파일을 삭제할 때 파일 데이터 전체를 삭제하는 것이 아닌, 파일의 헤더만 삭제하기 때문입니다.파일 헤더를 삭제한 파일 블록은 free block list에 저장되므로 복구가 가능합니다.

김진환

[인프런 워밍업 클럽 스터디 3기] 3주차 미션 - 자료구조와 알고리즘

1. 지금까지 배운 5개의 정렬 알고리즘의 장단점과 시간 복잡도를 적어주세요.퀵 정렬 - O(n log n)장점은 성능이 우수하며 정렬이 빠르다. 하지만 단점으론 이해와 구현이 어렵다.특히 퀵 정렬의 경우 병합 정렬과 달리 피벗이 배열을 반으로 가르지 않고, 한쪽에 쏠리게 되면 복잡도는 O(n^2)을 가지게 되어 정렬이 매우 느린 상황이 발생할 수 있다.병합정렬 - O(n log n)장점은 성능이 우수하며 정렬이 빠르다. 단점으론 이해와 구현이 어렵다.삽입정렬,선택정렬,버블정렬 - O(n^2)장점으론 병합, 퀵 정렬에 비해 이해와 구현이 쉽다.단점은 병합정렬과 퀵 정렬에 비해 상대적으로 느린 속도를 갖는다.  2. 메모리가 부족한 시스템에서 어떤 문제를 해결하는데 재귀로 쉽게 구현이 가능할 것 같습니다. 여러분이라면 메모이제이션과 타뷸레이션 중 어떤 걸 이용하실 건가요? 이유를 함께 적어주세요. 타뷸레이션을 사용하는게 좋다. 메모이제이션은 기존의 재귀와 달리 계산에 사용되는 값들을 저장해 놓는 방식이다. 이 저장으로 메모리를 통해 성능을 향상시킨다. 하지만 성능을 위해 메모리를 사용해서 저장한다. 비록 분할 정복을 해결 할때 재귀가 직관적이라면 메모이제이션이 더 유리하지만 메모리가 부족한 시스템이라면 메모리도 절약하면서 속도 역시 빠른 타뷸레이션을 사용하는게 더 좋은 방법이라고 생각한다.

김진환

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

메모리의 종류는 어떤것들이 있나요? 각 메모리의 특징도 함께 적어주세요.레지스터 - cpu내에 존재하며 가장 빠른 처리속도를 갖고 있다.캐시메모리 - cpu가 미리 가져온 데이터를 저장하는 곳이다. 성능의 이유로 여러개를 두며 단계에 따라 L1,L2,L3캐시로 나뉘어져 있다주기억장치 - OS와 다른 프로세스들이 실행시 올라가는 공간이다. 실행중인 프로그램들만 올라간다.보조기억장치 - 파일 및 프로그램등이 실행되지 않을때 저장되는 공간으로, 전원이 꺼진 후에도 데이터가 남아있어 주로 보관 및 기억용으로 사용된다. 사용자 프로세스가 메모리의 운영체제 영역에 침범하지 못하도록 만든 레지스터는 어떤 레지스터일까요? 경계레지스터. CPU내에 존재하며, 메모리 관리자가 사용자 프로세스가 해당 레지스터의 값을 벗어났는지 검사한다. 메모리 할당 방식에서 가변 분할 방식과 고정 분할 방식의 장단점은 뭔가요?가변 할당은 해당 메모리에 맞게 할당되므로, 내부 단편화 등의 공간 낭비가 없다. 단점으론 한 프로세스가 분산되어 할당되므로, 메모리 공간이 남아있어도 추가적 할당을 할 수 없는 외부 단편화가 발생하게 된다.고정 할당은 구현이 간단하고 오버헤드가 적다. 단점으로는 한 프로세스보다 더 큰 메모리가 할당될 수 있으며, 이로인해 특정 메모리보다 더 큰 공간이 할당되게 되는 내부 단편화가 발생한다. CPU 사용률을 올리기 위해 멀티프로그래밍을 올렸지만 스왑이 더 많이 이루어져 CPU 사용률이 0%에 가까워 지는 것을 뭐라고 할까요?스레싱.근본적으로 물리 메모리의 크기가 부족해서 일어나는 현상이며, SWAP시의 컨텍스트 스위칭으로 인해 발생한다. HDD나 SSD는 컴퓨터를 실행시키는데 꼭 필요한 걸까요?OS를 부팅하거나 윈도우,리눅스를 사용할 목적이라고 가정한다.HDD나 SSD외에 OS를 저장할 별도의 공간이 있다면 꼭 필요하지 않다.네트워크를 통한 부팅이나, 인터넷을 통한 원격 OS 부팅등을 사용해 부팅또한 가능하다. 따라서 HDD,SSD외에 OS가 저장되어있는 공간이 별도로 존재한다면 HDD나 SSD는 필요하지 않다. 파일을 삭제해도 포렌식으로 파일을 복구할 수 있는 이유가 무엇일까요?OS의 파일시스템에는 파일 관리를 위해 빈 공간을 모아둔 Free block list가 존재한다. 특정 파일이 삭제되면, 파일 시스템은 모든 정보를 지우는게 아니라 파일 테이블의 헤더를 삭제하고, 그 파일에 해당하는 블럭을 free block list에 추가한다.이렇게 되면 사용자는 파일이 지워진것 처럼 느끼게 된다. 하지만 실제 사용한 블록의 데이터는 지워지지 않고 그대로 남아 있으므로, 이후 포렌식을 통해 데이터 복구가 가능하다.

이태경

[인프런 워밍업 클럽 스터디 3기 - CS전공지식] 3주차 과제 - 자료구조와 알고리즘

자료구조와 알고리즘1. 지금까지 배운 5개의 정렬 알고리즘의 장단점과 시간 복잡도를 적어주세요.1) 버블정렬 : O(n²)장점 : 이해와 구현이 쉬움단점 : 성능이 O(n²)으로 좋지 않음2) 선택정렬 : O(n²)장점 : 이해와 구현이 쉬움단점 : 성능이 O(n²)으로 좋지 않음3) 삽입정렬 : O(n²)장점 : 이해와 구현이 쉬움단점 : 성능이 O(n²)으로 좋지 않음4) 병합정렬 : O(nlogn)장점 : O(nlogn)으로 성능이 좋음단점 : 이해와 구현이 어려움5) 퀵정렬 : O(nlogn)장점 : O(nlogn)으로 성능이 좋음단점 : 이해와 구현이 어려움2. 메모리가 부족한 시스템에서 어떤 문제를 해결하는데 재귀로 쉽게 구현이 가능할 것 같습니다. 여러분이라면 메모이제이션과 타뷸레이션 중 어떤 걸 이용하실 건가요? 이유를 함께 적어주세요.✨️ 타뷸레이션을 사용할 것 같습니다. 재귀로 쉽게 구현이 가능하더라도 메모리가 부족한 상황에서, 메모리를 사용하여 성능을 올리는 메모이제이션은 오히려 성능을 더 떨어 뜨릴 것으로 보이기 때문에 적합하지 않아 보입니다. 타뷸레이션은 상향식이지만, 메모제이션보다 적은 메모리와 빠른 속도로 빠르게 해결할 수 있기 때문입니다.

인프런워밍업클럽스터디3기CS전공지식

이태경

[인프런 워밍업 클럽 스터디 3기 - CS전공지식] 3주차 과제 - 운영체제

운영체제1. 메모리의 종류는 어떤것들이 있나요? 각 메모리의 특징도 함께 적어주세요.✨️ 레지스터휘발성 메모리메모리중 가장 빠름✨️ 캐시레지스터와 메인 메모리 사이의 속도 차이를 극복하기 위해 필요할 것 같은 데이터를 미라 저장 시킴✨️ 메인 메모리휘발성 메모리보조저장장치에 비해 빠르지만 비싸기 때문에 실행하는 프로그램만 올림✨️ 보조저장장치비휘발성 메모리메인메모리보다 저렴함 2. 사용자 프로세스가 메모리의 운영체제 영역에 침범하지 못하도록 만든 레지스터는 어떤 레지스터일까요?✨️ 경계 레지스터사용자 프로세스가 경계 레지스터의 값을 벗어났는지 감시하고 벗어났다면 프로세스을 강제 종료 시킴.3. 메모리 할당 방식에서 가변 분할 방식과 고정 분할 방식의 장단점은 뭔가요?✨️ 가변 분할 방식장점 : 프로세스의 크기만큼 할당 하기 때문에 공간 낭비가 없음.단점 : 외부 단편화 발생함.외부 단편화 : 프로세스가 작업을 끝내고 메모리에서 내려오면 메모리는 그 프로세스 크기만큼 공백이 발생하는데, 다음에 할당 요청을 하는 프로세스의 크기가 공백의 크기보다 커서 프로세스에게 할당하지 못하는 것✨️ 고정 분할 방식장점 : 구현이 간단하고 오버헤드가 적고 같은 크기로 나누기 때문에 관리하기 쉬움단점 : 내부 단편화 발생4. CPU 사용률을 올리기 위해 멀티프로그래밍을 올렸지만 스왑이 더 많이 이루어져 CPU 사용률이 0%에 가까워 지는 것을 뭐라고 할까요?✨️ 스레싱5. HDD나 SSD는 컴퓨터를 실행시키는데 꼭 필요한 걸까요? 이유를 함께 적어주세요.✨️ 네. 꼭 필요합니다.전원 공급 관점 : 보조저장장치(HDD나 SSD)는 전원이 공급되지 않아도 데이터가 유지 되는 비휘발성 메모리이지만, 메인 메모리(RAM)는 전원이 공급되지 않으면 데이터가 날라가는 휘발성 메모리이기 때문에 데이터를 유지할 수 없습니다.비휘발성 메모리엔 ROM 메모리도 있긴 하지만, ROM 메모리는 한번 쓰면 수정이 불가능하기 때문에 적합하지 않습니다.비용 관점 : 보조저장장치가 메인메모리보다 훨씬 저렴하기 때문에 비용면에서 효율적입니다.크기 관점 : 메인메모리 하나로 컴퓨터를 실행 시킨다면, 데이터 저장도 해야 하고, 프로세스가 올라갈 공간도 필요하기 때문에 아주 큰 크기가 필요할 것임. 또한 프로세스의 당장 실행 하지 않아도 될 부분까지 모두 올가야 하기 때문에 cpu 사용률도 떨어질 것이라 생각합니다.6. 파일을 삭제해도 포렌식으로 파일을 복구할 수 있는 이유가 무엇일까요?✨️ 파일을 삭제 할때 파일 테이블의 헤더만 삭제 되고 블록의 데이터는 남아 있기 때문에 포렌식으로 파일을 복구할 수 있습니다.

인프런워밍업클럽스터디3기CS전공지식

pej4303

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

1. 강의1.1 학습내용@RestControllerAdviceSpring에서 예외 처리를 전역적으로 관리할 수 있도록 도와주는 어노테이션이다.JSON 형태로 응답을 반환하는 데 최적화 되어 있다.@ExceptionHandler와 함께 사용하여 세부적인 특정 예외 유형 처리가 가능하다.@ExceptionHandler는 컨트롤러 내부에서 사용하면 개별 처리, @RestControllerAdvice와 함께 사용하면 전역 예외 처리 가능하다.여러 개의 예외를 한 메서드에서 처리 가능하다.@RestControllerAdvice class AdminApiControllerAdvice { val log = LoggerFactory.getLogger(AdminApiControllerAdvice::class.java) @ExceptionHandler fun handleException(e: AdminException):ResponseEntity<String> { log.info(e.message , e) return ResponseEntity.status(e.httpStatus).body(e.message) } @ExceptionHandler fun handleException(e: MethodArgumentNotValidException):ResponseEntity<String> { log.info(e.message , e) val fieldError = e.bindingResult.fieldErrors[0] val message = "[${fieldError.field} ${fieldError.defaultMessage}]" return ResponseEntity.badRequest().body(message) } }1.2 느낀 점템플릿 라이센스 문제로 이번주 봐야될 강의가 줄어들어 미션에 좀 더 집중 할 수 있었다.2. 미션2.1. 조회 REST API 만들기2.2. 삽입, 수정, 삭제 API 만들기테스트 케이스를 만들면서 상품, 주문 정보가 없는 경우 사용자 정의 에러를 만들어서 던지게 변경하였다.class OrderNotFoundException(val orderNo: Long) : CustomException("해당 주문정보가 없습니다. [주문번호 : $orderNo]")class ProductNotFoundException(val productCd: Long) : CustomException("해당 상품코드가 없습니다. [상품코드 : $productCd]")주문 등록  @Transactional fun create(requestDTO: RequestCreateDTO): ResponseEntity<ResponseDTO> { // 주문 마스터 생성 val orderHeader = Order(orderSts = OrderSts.PAYMENT_COMPLETED.code) orderRepository.save(orderHeader) // 주문 상세 생성 requestDTO.items.forEach { item -> val product = productRepository.findById(item.productCd).orElseThrow { ProductNotFoundException(item.productCd) // 상품 코드가 없을 경우 예외 던지기 } val orderDetail = OrderDetail( order = orderHeader, product = product, price = item.price, qty = item.qty, memo = item.memo ) orderDetailRepository.save(orderDetail) } val response = ResponseDTO( code = 200, msg = "주문 등록 성공", orderNo = orderHeader.orderNo, orderSts = orderHeader.orderSts ) return ResponseEntity.status(HttpStatus.OK).body(response) }주문 수정@Transactional fun update(orderNo: Long, requestDTO: RequestUpdateDTO): ResponseEntity<ResponseDTO> { // 주문 마스터 val orderHeader = orderRepository.findById(orderNo).orElseThrow { OrderNotFoundException(orderNo) // 주문 정보가 없을 경우 예외 던지기 } if ("60".equals(orderHeader.orderSts)) { val errorRes = ResponseDTO( code = 400, msg = "배송완료 상태는 수정 할 수 없습니다. [주문번호 : ${orderNo}]", orderNo = null, orderSts = null ) return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(errorRes) } if (requestDTO.orderSts > 0 ) { // 주문 상태가 유효한 코드인지 확인하고 적용 val orderSts = OrderSts.values().find { it.code == requestDTO.orderSts } ?: throw ApiException("유효하지 않은 주문 상태 코드입니다. [주문상태 : ${requestDTO.orderSts}]") // 주문 마스터 업데이트 orderHeader.orderSts = orderSts.code // code 값으로 업데이트 orderRepository.save(orderHeader) // 주문 상태를 업데이트 } // 주문 상세 val orderDetail = orderDetailRepository.findByOrderNo(orderNo) requestDTO.items.forEach { item -> val product = productRepository.findById(item.productCd).orElseThrow { ProductNotFoundException(item.productCd) // 상품 코드가 없을 경우 예외 던지기 } // 주문 상세 업데이트 val updateOrderDetail = orderDetail.find { it.product == product } if (updateOrderDetail != null) { updateOrderDetail.price = item.price updateOrderDetail.qty = item.qty updateOrderDetail.memo = item.memo orderDetailRepository.save(updateOrderDetail) // 업데이트 } } // 응답 반환 val response = ResponseDTO( code = 200, msg = "주문 수정 성공", orderNo = orderNo, orderSts = orderHeader.orderSts ) return ResponseEntity.status(HttpStatus.OK).body(response) }주문 삭제 @Transactional fun delete(orderNo: Long): ResponseEntity<ResponseDTO> { val orderHeader = orderRepository.findById(orderNo).orElseThrow { OrderNotFoundException(orderNo) // 주문 정보가 없을 경우 예외 던지기 } val orderSts = orderHeader.orderSts if ("60".equals(orderSts)) { val errorRes = ResponseDTO( code = 400, msg = "배송완료 상태는 삭제 할 수 없습니다. [주문번호 : ${orderNo}]", orderNo = null, orderSts = null ) return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(errorRes) } // 주문 마스터 orderHeader.delYn = "Y" orderRepository.save(orderHeader) // 주문 상세 val orderDetailList = orderDetailRepository.findByOrderNo(orderNo) orderDetailList.forEach { it.delYn = "Y" } // delYn 필드 값을 "Y"로 변경 orderDetailRepository.saveAll(orderDetailList) // 변경된 리스트를 저장 val response = ResponseDTO( code = 200, msg = "주문 삭제 성공", orderNo = orderNo, orderSts = null ) return ResponseEntity.status(HttpStatus.OK).body(response) }2.3. 느낀 점조회 API 만들기 미션은 제출하지 못했고 삽입, 수정, 삭제 API 만들기 미션은 겨우 제출하였다.생각보다 내가 원하는 기능은 구현하는게 빠르게 되지 않아서 많이 검색해보았다. 개발하고 나서 사용하지 않는 테이블과 컬럼들을 정리하였다. 초기 ERD와 좀 다르게 되어서 ERD도 다시 수정하였다.출처https://www.inflearn.com/course/%EC%9E%85%EB%AC%B8%EC%9E%90-spring-boot-kotlin-%ED%8F%AC%ED%8A%B8%ED%8F%B4%EB%A6%AC%EC%98%A4/dashboard

백엔드발자국인프런워밍업클럽

강현

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

강의 수강섹션3. 단위 테스트이전에 프로젝트에서도 사용해본 적이 있는 단위테스트와 JUnit에 대해 배웠다. 해본 적이 있지만, 다시 막상 보니 새롭게 느껴져서 복습하듯이 학습했다.테스트하기 어려운 영역에 대해, 이전에는 해당 테스트는 그냥 사람이 직접 테스트하는 것으로 구현했었다. 하지만 이번 강의를 통해 밖으로 파라미터를 주입시켜주어 순수 함수로 만들어주면, 테스트 코드를 작성할 수 있다는 것을 알게 되었다.섹션4. TDDTDD를 이론으로만 공부해봤지 직접 코드를 작성하는 것은 처음이었다. 이론으로만 배웠을 때는 너무 멀게만 느껴졌는데, 직접 코드를 따라치니까 더 TDD가 무엇인가에 대한 개념이 가까워지는 것 같다.TDD 코드를 따라치면서 느낀 것이, TDD를 지키지 않고 개발할 때보다 코드 파일들을 많이 왔다갔다 하는 것이라고 체감이 들었다. TDD를 지킬 때에는 내가 무엇을 개발하고 있었는가에 대해 계속 의식해주어야 될 것같다는 생각이 들었다. 강의를 들을 때에는 사용하지 않았지만, 내가 직접 TDD를 스스로 한다면, Red Green refactor을 하는 과정에 주석에 todo 표시를 자꾸 해줘야 되겠다.섹션5. 테스트는 [ ]다.👍 DisplayName을 "ㅇㅇ 테스트"라고 명명하는 습관이 있었는데, 강의 시작하자 마자 지적받아서 뜨끔했다. 이제 절대로 저렇게 작명하지 않을 것이다.👍 given, when, then을 나누어 작성하는 BDD에 대해 배웠다. 아직 given, when, then에 어떤 명령어가 해당 되는지 헷갈려서 익숙치 않다. 계속 연습해봐야겠다.섹션6. Spring & JPA 기반 테스트👍 @SpringBootTest와 @DataJpaTest 의 가장 큰 차이는 @DataJpaTest는 @Transactional을 포함하고 있다는 것이다.@SpringBootTest에 @Transactional 추가해주면, @AfterEach 함수로 데이터 초기화 안해줘도 되는데 이러면 서비스 클래스에 @Transactional이 안붙어있어도 테스트코드가 통과되는 문제가 있다. 이거 알고 쓰자.👍 서비스 클래스에 @Transactional(readonly = true) 를 걸고, CUD 메소드에 한해서 @Transactional 을 해주자.👍 ㅇㅇ 생성 함수에서는 동시성을 고려해주어야 한다.함수가 호출되는 빈도수가 낮으면? 👉 ㅇㅇ의 id에 unique 제약조건 걸고, 실패 시 재시도하는 로직을 추가함수 호출 빈도가 높으면? 👉 ㅇㅇ의 id를 +1씩 증가하는 값이 아니라, UUID같은 것으로 대체미션Day11역시 저번 실습 미션처럼 강의를 보며 따라칠때는 너무 쉬운 것 같은데 직접 치려니 힘들다. 특히 어떤 메소드에 대해 테스트코드를 작성해야하는가 판단하는 부분이 가장 어려웠다.이미 구현 코드는 작성되어 있으니 given, when, then을 나누어 DDD를 지켜 코드를 작성했다. 각 명령어들이 given, when, then 어느쪽에 속해있는지 판단하는 것이 아직 어렵다. 그래도 강의 맨 처음에 지적받은 DisplayName 명명 규칙은 열심히 지킨 것 같다.

백엔드

wonderson

[워밍업 클럽 3기 CS 전공지식] 3주차 자료구조와 알고리즘 미션

지금까지 배운 5개의 정렬 알고리즘의 장단점과 시간 복잡도를 적어주세요.버블 정렬앞에 있는 숫자와 옆에 숫자를 비교해서 자리를 바꾸는 알고리즘장점이해가 쉽고 구현이 간단합니다. 단점O(n^2) 으로 성능이 좋지 않습니다.시간 복잡도이중 for문이 핵심 로직으로 O(n^2) 입니다.선택 정렬정렬된 영역과 정렬되지 않은 영역을 나눠서 정렬되지 않은 영역을 비교하는 알고리즘장점이해가 쉽고 구현이 간단합니다. 단점O(n^2) 으로 성능이 좋지 않습니다.시간 복잡도이중 for문이 핵심 로직으로 O(n^2) 입니다.삽입 정렬정렬되지 않은 영역에서 데이터(가장 앞에 있는 숫자)를 하나씩 꺼내서 정렬된 영역 내 적절한 위치에 "삽입"해서 정렬하는 알고리즘장점이해가 쉽고 구현이 간단합니다. 단점O(n^2) 으로 성능이 좋지 않습니다.시간 복잡도이중 for문이 핵심 로직으로 O(n^2) 입니다.병합 정렬재귀를 이용해 정렬하는 알고리즘 (분할 정복) 장점 O(nlogn) 으로 성능이 좋습니다. 단점재귀적인 기법으로 이해하기 어렵습니다.구현이 어렵다.시간 복잡도각 단계를 거칠 때마다 영역의 수가 반으로 줄기 때문에 logn분할된 배열을 병합할 때는 n개의 데이터를 n번 비교=> n, nlogn 곱해서 O(nlogn) 성능이 나옵니다.퀵 정렬재귀를 이용해 정렬하는 알고리즘 (분할 정복)한 번 진행될 때마다 피벗이 정렬되고 정렬된 배열을 좌우로 나눠서 같은 방식으로 재귀 호출을 해 모든 배열을 정렬합니다. 장점 O(nlogn) 으로 성능이 좋습니다. 단점재귀적인 기법으로 이해하기 어렵습니다.구현이 어렵습니다.시간 복잡도피벗을 기준으로 반을 나눕니다. -> 수가 1개 남을 때까지 반으로 나눕니다. logn나눠진 단계를 배열의 원소 수 n만큼 진행=> n, nlogn 곱해서 O(nlogn) 성능이 나온다. 메모리가 부족한 시스템에서 어떤 문제를 해결하는데 재귀로 쉽게 구현이 가능할 것 같습니다. 여러분이라면 메모이제이션과 타뷸레이션 중 어떤 걸 이용하실 건가요? 이유를 함께 적어주세요.재귀로 쉽게 구현이 가능하다고 했으니 먼저 메모이제이션을 이용해서 문제를 해결할 거 같습니다.구현을 하고 실행했을 때 메모리를 어느 만큼 사용하는지 측정해보고 계속 사용할 지 판단하겠습니다.메모제이션으로 구현을 했는데 메모리가 부족하다면 그 때 타뷸레이션을 이용해 문제를 해결하겠습니다.   ※ 출처[인프런 / 그림으로 쉽게 배우는 자료구조와 알고리즘 (감자) / 섹션 3 (유닛 6~10)]

채널톡 아이콘