블로그

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

회고성격상 한번에 두 개 하는 걸 힘들어 하는 스타일이라 이번주는 나름의 결단으로 테스트 강의 듣는데에 거의 올인했다. (월요일 오전에만 좀 코테 공부함) 내가 하는 공부인 코테 공부랑 워밍업 클럽 둘 중 하나가 시간을 적게 써도 충분히 되는 거였다면 둘 다 병행을 해도 감당할 수 있었겠지만 워밍업 클럽을 진행해보니 이거에도 시간을 많이 써야겠다는 판단을 하게 됐다.그래서 맨 마지막 "테스트를 작성하는 마음가짐"이라는 마지막 강의를 제외하고서는 남은 모든 강의를 다 들었다.아직 뭔가 프로젝트 하나 해보지 못한 나는 당연히 테스트 코드를 스스로 짜본적이 없었고, 그래서 이 강의를 듣고 싶었는데, 다 듣고 나니까 추후에 프로젝트를 진행할 때, 어떤 식으로 테스트코드를 짜고 테스트코드를 바탕으로 프로덕션 코드를 짜야 될지에 대해 감을 잡을 수 있었다.다음주에는 복습하면서 이번주에 했던 11일차 미션했던 내용도 한번 배운 내용을 토대로 수정해봐야겠다.미션 Day 11https://github.com/myc0603/readable-code/tree/main/src/test/java/cleancode저번 리팩토링 강의를 통해 작성했던 지뢰찾기 코드와 스터디카페 코드 중 하나를 선택해서 단위테스트를 작성하는 것이 미션이었다.코드 자체가 스터디카페보다는 지뢰찾기도 좀 더 복잡했던 거 같아서 지뢰찾기를 선택해서 테스트 코드를 진행하였다.작성했던 클래스들과 메서드들을 하나하나 보면서 테스트해볼 수 있는 코드가 뭐가 있을까 보면서 테스트 코드를 작성해보았다. 하다보니 이걸 어떻게 테스트 해야되지? 라는 의문도 생기고 이것도 테스트를 해야되나? 라는 의문도 생기는 등 여러 의문이 생겼는데 강의를 듣다 보니 미션을 하면서 생겼던 의문 중 해소되었다.다음주에는 끝까지 들은 강의 내용을 바탕으로 테스트 코드를 다시 더 좋게 발전시켜볼 수 있을 거 같다.강의 내용Layer별 테스트Persistence Layer: 거의 단위테스트 느낌 얘만 써서 테스트 하면 됨Business Layer: Persistence Layer랑 같이 묶어서 통합 테스트 진행Presentation Layer: Business Layer, Persistence Layer를 Mocking해서 테스트 진행, 그냥 http요청을 보냈을 때 ok 응답이 오는 지 정도 확인 productNumber, 재고 등등.. -> 동시성 이슈 고려Transactional Transaction "readOnly" - CQRS : command(CUD)와 query(Read)를 분리서비스나 DB를 read용, write용 따로 만들 수 있음 JPA 사용시 객체를 캡쳐하는 등의 로직을 read만 할 때는 사용 안 할 수 있음테스트에서 사용하게 되면 프로덕션 코드에서 Transactional을 잘 사용했는 지에 대한 테스트가 안 되니 이점 유의해서 사용validation : 뭐에 대한 유효성 검증이냐에 따라 컨트롤러에서 검증할지 서비스에서 검증할지 등 고민해볼 필요가 있다. Mocking1. 가짜 객체를 만들어 사용 -> 필요한 경우 원하는 행위를 정해줄 수 있음 (Stubbing) 2. 원하는 메서드를 제외하고서는 진짜 객체처럼 행위를 원하면 Spy객체를 만들어 사용 3. Mocking을 어느 정도까지 할 지에 대해 고민해봐야 한다하나의 테스트는 하나만 검증!하지만 정말 하나에 테스트에서 딱 하나씩만 검증하다보면 테스트 별로 묶고 싶어지는 테스트가 생기기 마련=> @ParameterizedTest, @DynamicTest 사용!!완벽하게 제어하기컨트롤하지 못하는 값 사용을 지양, 외부 시스템 Mocking테스트 세팅 Test Fixture각 테스트에서 세팅 vs @BeforeEach/@BeforeAll 세팅을 하기위한 세팅 같은 너무 기본적인 세팅이면 @BeforeEach/@BeforeAll, 웬만하면 각 테스트 코드에서 세팅Test Fixture 클렌징deleteAll() vs deleteAllInBatch()테스트 환경 통합서버 띄우는 횟수를 줄여 테스트 코드 돌리는데 드는 시간 단축학습테스트학습에 테스트 활용Spring REST Docs이거 진짜 유용해보이는데 제대로 복습하고 써봐야겠다

백엔드

인프런 워밍업 클럽 스터디 3기 - Backend <3주 발자국>

package com.example.portfolio.presentation.controller import com.example.portfolio.presentation.dto.* import com.example.portfolio.presentation.service.PresentationService import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.RequestMapping import org.springframework.web.bind.annotation.RestController @RestController @RequestMapping("/api") class PresentationApiController( private val presentationService: PresentationService ) { // rest controller이기 때문에 httml response body에 리턴한다. @GetMapping("/test") //== @RequestMapping(method = [RequestMethod.GET], name = "/test") // 이 외에도 postMappring과 PutMapping이 존재한다. fun test(): String { return "OK" } @GetMapping("/v1/introductions") fun getIntroductions(): List<IntroductionDTO> { // 중요한 작업들은 서비스에서 다 해준다. return presentationService.getIntroductions() } @GetMapping("/v1/links") fun getLinks(): List<LinkDTO> { return presentationService.getLinks() } @GetMapping("/v1/resume") fun getResume(): ResumeDTO { return presentationService.getResume() } @GetMapping("/v1/projects") fun getProjects(): List<ProjectDTO> { return presentationService.getProjects() } @GetMapping("/v1/articles") fun getArticles(): List<ArticleDTO> { return presentationService.getArticles() } }package com.example.portfolio.presentation.repository import com.example.portfolio.domain.entity.* import com.example.portfolio.domain.repository.* import org.springframework.stereotype.Repository import java.security.PrivateKey @Repository class PresentationRepository( private val achievementRepository: AchievementRepository, private val introductionRepository: IntroductionRepository, private val linkRepository: LinkRepository, private val skillRepository: SkillRepository, private val projectRepository: ProjectRepository, private val experienceRepository: ExperienceRepository, private val articleRepository: ArticleRepository ) { fun getActiveAchivements(): List<Achievement> { return achievementRepository.findAllByIsActive(true) } fun getActiveExperiences(): List<Experience> { return experienceRepository.findAllByIsActive(true) } fun getActiveIntroductions(): List<Introduction> { return introductionRepository.findAllByIsActive(true) } fun getActiveLinks(): List<Link> { return linkRepository.findAllByIsActive(true) } fun getActiveProjects(): List<Project> { return projectRepository.findAllByIsActive(true) } fun getActiveSkills(): List<Skill> { return skillRepository.findAllByIsActive(true) } fun getArticles(): List<Article> { return articleRepository.findAll() } fun setArciels() { } } package com.example.portfolio.presentation.interceptor import org.springframework.context.annotation.Configuration import org.springframework.web.servlet.config.annotation.InterceptorRegistry import org.springframework.web.servlet.config.annotation.WebMvcConfigurer @Configuration class PresentationInterceptorConfiguration( private val presentationInterceptor: PresentationInterceptor ) :WebMvcConfigurer { override fun addInterceptors(registry: InterceptorRegistry) { registry.addInterceptor(presentationInterceptor) .addPathPatterns("/**") .excludePathPatterns("/assets/**", "/css/**", "/js/**", "/admin/**", "h2**", "/favicon.ico", "/error") } }  느낀 점프리젠테이션 부분에서 화면과 인터셉터 설계를 하면서 아직 화면 구성과 데이터 조작에 어려움을 느껴서 진도를 많이 못나갔다. 지금부터 복습 다시하면서 천천히 자기서으로 만들어야 한다는 시간을 가져야 겠다고 생각했다.

박윤영

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

 해당 글은 ‘입문자를 위한 Spring Boot with Kotlin - 나만의 포트폴리오 사이트 만들기(정보근)’ 강의 를 수강하고 작성한 내용입니다.https://www.inflearn.com/course/입문자-spring-boot-kotlin-포트폴리오/dashboard📝 강의 내용 정리[실습] 공통 개발 - Exception과 Advice오류의 종류Throwable: 오류 최상위 클래스Error: 애플리케이션이 대응할 수 없는 오류Exception: 애플리케이션에서 대응할 수 있는 오류(개발자가 예외 처리를 해줘야함)UncheckedException: RuntimeException을 상속하는 모든 예외, Transactional에서 롤백의 대상이 됨CheckedException: Exception을 상속하며 RuntimeException이 아닌 모든 예외. 롤백을 하고 싶은 경우 별도로 지정해줘야하고 try-catch를 이용해 반드시 예외처리를 해줘야함관련 어노테이션@ExceptionHandler: 컨트롤러에서 던지 예외를 잡아 처리해주는 역할. 컨트롤러마다 예외를 처리하는 중복 코드를 작성할 필요없이, 같은 예외를 공통적으로 처리할 수 있음. (컨트롤러 클래스에 선언할 경우에만 동작!)@ControllerAdvice: 범위 내의 모든 컨트롤러 클래스에 @ExceptionHandler를 공통적으로 적용해줌. 컨트롤러 클래스는 세분화 되었지만, 예외는 똑같이 처리해야 할 경우 사용. 예외별로 다른 뷰를 리턴할 수 있으며 @ResponseBody를 붙여 리턴 값으로 응답할 수 도있음@RestControllerAdvice: @ControllerAdvice와 @ResponseBody가 같이 선언되어 있음코드 설명AdminApiControllerAdvice인터셉터와 비슷, 예외처리에 특화된 인터셉터컨트롤러가 예외를 던지면 해당 예외를 잡아서 대응해주는 컨트롤러@ExceptionHandler fun handleException(e: AdminException): ResponseEntity<String>{ } 해당 메서드를 컨트롤러안에 넣어도 동작하는데, 공통된 에러 처리를 위해서 따로 빼는게 좋음(중복 방지, 관리 용이)처리할 에러 종류→3가지AdminException: 개발자가 정의하여 의도적으로 던지는 AdminException 처리용MethodArgumentNotValidException: Validation에서 던지는 예외를 처리, 클라이언트에서 보낸 데이터 오류이기 때문에 BadRequest응답Exception: 그 외 예외 처리. “시스템 오류”라는 메세지는 클라이언트쪽에 구체적인 오류를 숨기는 목적[실습] 공통 개발 - DTOApiResponsecompanion object{}: 이 안에 정의되는 메서드들은 static 메서드가 됨static으로 생성한 이유: 여러 컨트롤러에서 동일한 응답을 편하게 사용하기 위해데이터 저장, 수정, 삭제에 대한 메세지 지정FormElementDTOhtml에 input 태그에 들어갈 유형을 정해주는 클래스(텍스트, 데이트, 셀렉트)서버에서 넘겨준 form을 보고 프론트에서 어떤 형태로 form을 만들지 선택할 수 있도록⇒ 이게 여기서 화면까지 그려주기때문에 필요한 기능 같음TableDTO서비스 단에서 클라이언트로 줄 응답을 만들때 여기 DTO통해서 주면 얘가 알아서 형식 맞춰주는 DTOvarag: 그냥 “a”,”b” 형태로 줘도 [”a”,”b”] 같이 리스트로 처리해줌필드 종류name: 테이블의 이름columns: 테이블이 갖고 있는 컬럼들records: 각 레코드들동작 방식테이블의 컬럼 정보와 레코드 정보를 엔티티 클래스에서 받아옴ex) Link 테이블 값을 조회한다고 하면name: Link columns:[link_id, name, content, is_active, created_Date_time , updated_date_time] records:[ [1, "Github","<http://ddd>", true, 날짜, 날짜], [2, "Instagram","<http://ddd>", true, 날짜, 날짜] ] [실습] 공통 개발 - 인터셉터어드민의 사이드바 메뉴를 프론트가 아닌 서버에서 관리하기 위함 목적PageDTO: 소메뉴, 메뉴명과 해당 페이지로 이동하는 경로를 가짐MenuDTO: 대메뉴, 메뉴명과 포함된 소메뉴를 가짐AdminInterceptor위의 MenuDTO와 PageDTO를 생성하여 Model에 넣어주는 역할AdminInterceptorConfiguration인터셉터를 설정/admin 이하의 모든 경로에 대해 동작하도록[실습] 조회 개발 - 연관관계 없음서비스에서 repository를 바로 주입해서 쓰는것과 이전의 퍼싸드 패턴 둘중에 뭐가 더 좋을건가..서비스에서 DTO로 변환할때 TableDTO 사용TableDTO안에 만든 from 메서드가 클래스 info와 entity만 넣으면 dto로 변환해줌controller먼저 Form 요소 세팅 → model에 넣어줌서비스에서 테이블 정보 받아오기 → model에 넣어줌[실습] 조회 개발 - 연관관계 있음getExperienceDetailTable 메서드 설명모든 experienceDetail 을 다 조회해서 화면에 뿌려주지 않고, 컨트롤러에서 상세조회 값이 true인 애들만 상세조회 버튼이 노출되고, 해당 버튼을 누르면 여기 메서드로 들어와 디테일 값을 넘겨주는 방식(매개변수 id가 nullable한 이유: 상세조회 버튼 누르기 전에 빈 리스트를 먼저 주기 위해서)[실습] 삽입, 수정 API 개발 - 연관관계 없음@NotBlank: 해당 필드가 비어있지 않아야 함을 나타냄, 비어있으면 예외 발생두번째 toEntity 메서드 → 엔티티 수정 용(엔티티 수정 시엔 해당 메서드 호출)컨트롤러에서 데이터 받아올 때, data class로 DTO받아온 다음에 해당 데이터를 toEntity()메서드를 이용해서 엔티티형으로 변환, 해당 엔티티 값을 레포지토리에 저장[실습] 삽입, 수정, 조회 API 개발 - 연관관계 일대다valid 종류@field:Positive값이 0보다 커야함@field:Min(value = 정수), @field:Max (value = 정수)값의 최대, 최솟값 정의val detailMap = experience.details.map { [it.id](<http://it.id/>) to it }.toMap(){id: experienceDetail} 형식으로 만들어줌experience서비스의 update메서드에 repository.save()메서드를 호출하지 않는 이유 → JPA더티체킹에 의해 트랜잭션이 종료될 때 업데이트가 자동으로 반영됨.✅미션[미션4] 조회 REST API 만들기일단 로그인 없이 게시글을 조회해오는 부분만 구현했다. 게시물 조회는 목록 조회와 상세 조회 두가지를 구현했다코틀린으로 구현을 하다보니 에러처리를 어떻게 할지 몰라 애를 좀 먹었었다. 현재는 exceptionHandler를 이용해 IllegalState예외만 잡아놓게 처리해놨는데, 강의를 보니 여러 에러들과 200코드를 한번에 처리하는 방법이 있었다. 미션 후 적용해 볼 생각이다.미션 주제에 한 api에 테스트를 3개 작성하라는 내용이 있었다. 어떤 테스트를 진행할까 고민하다가, 일단 각 기능이 제대로 동작하는지 성공 테스트 2개와 존재하지 않는 아이디로 조회를 할 때 발생하는 예외 테스트를 진행해 보았다. 기능이 좀 더 구체화 되면 그에 따라 테스트도 늘 예정이다.[미션5] 삽입, 수정, 삭제 REST API 만들기삽입, 수정, 삭제 기능을 구현하려고 보니 조회기능 구현할땐 발목을 잡지 않았던 로그인이 문제였다. 원래 계획은 스프링 시큐리티를 붙이는 거였어서 붙이고 나면 현재 로그인된 멤버값을 받아오는건 일이 아니였는데 로그인이 구현되어있지 않는 상태에서 멤버값을 어떻게 가져오지가 걱정이였다. 맨처음 삽입 기능만 구현할 때는 임시로 Path에 받아오도록 구현했었는데 아무리 임시라고 해도 보안상에 좋지 않을꺼라는 생각이 들었다. 그래서 그나만 안전한 RequestBody에 memberId를 받아오는 걸로 구현해 놓았다.삽입기능은 많이 복잡한 기능이 아니다보니 수월하게 작성할 수 있었는데 수정에서 문제가 발생했다. 강의에선 더티체크를 통해 값을 save하지 않아도 jpa가 레포지토리에 업데이트를 진행한다고 했었는데 테스트를 해보니 값이 변경되지 않았다. 이유를 찾아보니 메서드위에 Transactional 어노테이션을 누락했었다. 어노테이션 추가하고 나선 값이 제대로 변경되는 것이 확인되었다.삭제 기능에서도 memberId를 전달하는 것이 문제가되었다. 수정이나 삽입은 body값이 존재하니 거기에 숨겨서 보내면 됬는데 삭제에서 requestBody를 쓰는건 Rest에 위반이 될것같았다. 그래서 하는 수 없이 삭제에서는 param값으로 memberId를 받아오는 형식으로 구현해놓았다.테스트 작업은 일단 각 기능들이 정상적으로 동작하는지와 프로젝트 주제에 관리자와 사용자 권한 내용이 있기때문에 현재 사용자가 권한이 있는 사용자인지를 테스트하는 식으로 진행하였다.📅3주차 회고강의에서 어드민 개발을 시작하면서 확실히 구현하는 기능이 많아졌다. 그리고 DTO를 통합해 데이터를 한번에 변환하는 것과 에러와 성공 관련 상태코드들을 한곳에서 처리하는 부분도 다뤄져있어서 유익했다. 그동안 프로젝트에서는 예외처리 부분 코드를 가져와서 사용했었는데, 이번 기회에 직접 작성해보니 핸드러 흐름을 알기에 좋았던 것 같다.미션을 진행하면서 아쉬운 부분이 많이 느껴졌다. 좀 다급하게 개발을 하다보니 구현하면서도 이게 맞아..? 하는 부분들이 여럿있었고, 좀더 기능을 구체화해서 리팩토링을 해봐야겠다는 생각이 들었다. 어찌보면 간단한 기능들인데도 막상 구현하려니 쉽지는 않아서 힘들었다. 그래도 하나씩 기능이 구현될 때마다 재미는 있었다. 워밍업 클럽이 끝나고 나서도 기능 구체화를 위해 개발과 리팩토링을 꾸준히 진행할 예정이다.

구탱

인프런 워밍업 클럽 스터디 3기 - CS 전공지식 (운영체제, 자료구조, 알고리즘) 미션

운영체제 메모리의 종류는 어떤것들이 있나요? 각 메모리의 특징도 함께 적어주세요.주기억장치(RAM): 실행 중인 프로그램과 데이터를 저장하는 공간, 속도가 빠름, 전원이 꺼지면 데이터가 소멸된다보조기억장치(HDD/SSD): 데이터를 영구적으로 저장하는 공간, 속도가 느림, 비휘발성이라 전원이 꺼져도 데이터 유지된다가상 메모리(Virtual Memory): RAM이 부족할 때 보조기억장치를 임시 메모리처럼 사용한다, 실행 가능한 프로그램 크기를 확장할 수 있다레지스터(Register): CPU 내부에 있다, 용량이 작고 가장 빠른 속도, 연산이나 명령어를 실행하는 것에서 직접 사용된다캐시 메모리(Cache Memory): CPU와 메인 메모리 사이에 있고 속도를 높이는 역할을 한다, 자주 사용하는 데이터를 저장하고 빠르게 접근할 수 있다 사용자 프로세스가 메모리의 운영체제 영역에 침범하지 못하도록 만든 레지스터는 어떤 레지스터일까요?-> 경계레지스터: CPU내에 존재하며, 메모리 관리자가 사용자 프로세스가 경계레지스터의 값을 벗어났는지 검사하고 만약 벗어난 경우 그 프로세스를 종료한다. 메모리 할당 방식에서 가변 분할 방식과 고정 분할 방식의 장단점은 뭔가요?-> 가변분할방식은 메모리를 동적으로 할당해서 공간을 활용하는 것이 유용한 장점이 있다. 하지만 연속된 공간이 부족한 경우에는 압축이 필요하다.-> 고정분할방식은 관리가 간편하고 쉽다는 장점이 있지만, 프로세스 크기에 따라서 미리 메모리를 나눠야 하기 때문에 공간의 낭비가 심하다. CPU 사용률을 올리기 위해 멀티프로그래밍을 올렸지만 스왑이 더 많이 이루어져 CPU 사용률이 0%에 가까워 지는 것을 뭐라고 할까요? -> 스레싱  5. HDD나 SSD는 컴퓨터를 실행시키는데 꼭 필요한 걸까요? 이유를 함께 적어주세요. -> 필수적이지는 않지만, OS가 저장되어있지 않으면 부팅이 불가능하므로 일반적으로는 파일을 삭제해도 포렌식으로 파일을 복구할 수 있는 이유가 무엇일까요?-> 실제 데이터가 삭제되지 않고 사용했던 블록의 데이터가 그대로 남아있기 때문이다. 따라서 새로운 데이터를 덮어쓰기 전에는 복구할 수 있다.자료구조와 알고리즘1. 지금까지 배운 5개의 정렬 알고리즘의 장단점과 시간 복잡도를 적어주세요.버블정렬: 이해가 쉽고 구현이 간단, 성능이 좋지못함, O(n²)선택정렬: 이해가 쉽고 구현이 간단, 성능이 좋지못함, O(n²)삽입정렬: 이해가 쉽고 구현이 간단, 성능이 좋지못함, O(n²)병합정렬: 이해와 구현이 어려움, 성능이 좋음, O(n log n)퀵정렬: 이해와 구현이 어려움, 성능이 좋음, 평균적으로 O(n log n), 최악의 경우는 O(n²)메모리가 부족한 시스템에서 어떤 문제를 해결하는데 재귀로 쉽게 구현이 가능할 것 같습니다. 여러분이라면 메모이제이션과 타뷸레이션 중 어떤 걸 이용하실 건가요? 이유를 함께 적어주세요.-> 메모리가 부족하다면 타뷸레이션을 이용한다. 왜냐하면 메모리를 최소로 사용할 수 있고, 빠른 속도로 해결할 수 있기 때문이다.

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

회고록3주차로 넘어가면서 벌써 한 강의가 끝났다. 언제나 로직을 구현하고 테스트 코드를 구현하는 것은 어렵고도 귀찮은 일이다 이주 과제인 테스트 코드 과제는 나에게 어떤 의미가 있었나 생각해보는 시간이 필요하다. 1. 무엇을 배웠나?이번 주에는 다음과 같은 내용을 학습했다. 섹션 3 단위 테스트테스트 도구JUnit 5: 단위 테스트를 위한 프레임워크 (XUnit 계열)AssertJ: 풍부한 API 및 메서드 체이닝 지원, 테스트 코드 작성 보조  테스트 케이스 세분화해피 케이스: 정상적인 상황에서 예상된 동작 확인예외 케이스: 에러나 예외 상황 검증경계값 테스트: 특정 범위(이상, 이하, 초과, 미만)나 날짜 등 경계값 테스트 테스트하기 어려운 영역 구분 및 분리테스트하기 어려운 코드 예시현재 날짜/시간, 랜덤 값, 전역 변수/함수, 사용자 입력 등표준 출력, 메시지 발송, DB 기록 등 외부 세계에 영향을 주는 코드해결 방법: 테스트하기 어려운 요소를 외부로 분리하여 테스트 가능한 코드로 변환 순수 함수(Pure Function)같은 입력에는 항상 같은 결과를 반환하며, 외부 세계와 단절된 형태 → 테스트하기 쉬운 코드섹션 4 TDDTDD의 핵심 사이클TDD는 RED → GREEN → REFACTOR 3단계로 반복 수행됨.RED (실패하는 테스트 작성)새로운 기능을 추가하기 전에 먼저 실패하는 테스트를 작성GREEN (테스트 통과, 최소한의 코드 작성)테스트를 통과할 수 있도록 최소한의 코드 작성REFACTOR (구현 코드 개선, 테스트 유지)중복 제거, 성능 개선 등의 최적화 수행테스트는 계속 통과해야 함 2. 무엇이 인상적이었나?이번 주 학습 중 가장 인상 깊었던 내용 보다는 궁금한 점이 있었다. 기존 레거시 리펙토링에 대한 테스트 보장을 어떻게 할 것인가? 해당 내용에 대한 해결책은 차주 내용에 포함시켜 보겠다.3. 이번 주 학습을 통해 얻은 것전반적으로 기존의 인지하고 있는 내용을 반복하는 시간이 되었다. 차주에도 꾸준히 이어가길... 4. 다음 주 목표금주 발생한 궁금증을 해소시켜 보기 

제갈진우

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

프로덕트 : 시뮬레이션 게임 초기 성장 루프 완료율정의 및 계산식튜토리얼 완료 후 3일 내 주요 성장 루프(건물 업그레이드, 생산, 수확 등) 1회 이상 완료한 비율growth_loop_complete_user / 신규 유저 수측정 이유유저가 ‘성장 구조’를 이해하고 체험했는지 확인활용 방안초반 구조 이해 못한 유저 → 성장 보상 강화 / 콘텐츠 설명 보완 자원 인플레이션 속도정의 및 계산식일정 기간 내 자원 총량 증가율(일간 or 주간) 값이 1 이상이면 자원 축적됨총 자원 생산량 ÷ 총 자원 소비량측정 이유게임 내 경제 밸런스 체크정의 및 계산식인플레이션이 심하면 → 자원 소각 요소 추가 필요 콘텐츠 소화 속도 분포정의 및 계산식각 스테이지/레벨 도달까지 평균 걸린 일수 or 세션 수측정 이유평균 유저보다 너무 빠르거나 느린 구간 식별활용 방안해당 구간 난이도 / 리소스 요구량 조정 최초 자원 부족 포인트정의 및 계산식유저가 처음으로 자원이 부족해 진행이 막힌 시간대 or 구간최초 event_resource_lack 발생 시간 or 레벨측정 이유리텐션 저하 지점 파악활용 방안첫 불편 포인트 파악 → 보상/가이드 삽입 이탈 전 행동 패턴 시퀀스정의 및 계산식3일 이상 미접속 유저 기준 마지막 N개 행동 로그 시퀀스 분석측정 이유어떤 루틴 중단 후 이탈이 많은지 분석활용 방안이탈 직전 주요 행동 흐름 분석 → 잔존 유도 시점 설정 건설 슬롯 포화율정의 및 계산식건설/업그레이드 가능한 슬롯 수 대비 실제 사용률 (or 슬롯 사용률 100% 유지 비율)슬롯 포화 횟수 ÷ 총 건설 시도 횟수측정 이유유저가 시스템 제한에 막혀 멈췄는지 확인활용 방안진행 병목 판단 → 슬롯 해방 상품 or 슬롯 보상 제공 미션 건너뛰기율정의 및 계산식주간 미션 중 완료되지 않은 미션 비율 (유저 당)미완료 미션 수 ÷ 전체 주간 미션 수측정 이유미션 설계 난이도 및 유의미성 판단활용 방안유저가 무시하는 미션 식별 → 난이도/보상 리뉴얼

기획 · PM· PO지표PMPO워밍업클럽인프런데이터분석게임지표

taeminseo

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

3주차 미션운영체제메모리의 종류는 어떤것들이 있나요? 각 메모리의 특징도 함께 적어주세요.레지스터CPU 내부에 있는 초고속 메모리용량이 적고 가격이 비쌈휘발성의 성질을 가져 컴퓨터가 종료되면 데이터도 같이 사라짐캐시 메모리CPU와 RAM 사이에서 데이터 속도를 높이기 위한 메모리메인 메모리와 레지스터 사이 속도 차이가 있어서 필요한 데이터를 미리 가져와 저장하는 공간메인 메모리실행 중인 프로그램과 데이터를 저장휘발성 메모리보조기억장치데이터를 영구 저장하는 장치 (비휘발성)속도는 RAM보다 느리지만 대용량 저장 가능사용자 프로세스가 메모리의 운영체제 영역에 침범하지 못하도록 만든 레지스터는 어떤 레지스터일까요?경계 레지스터프로세스가 접근할 수 있는 메모리의 최대 범위를 저장경계 레지스터 값을 벗어나면 프로세스를 종료메모리 할당 방식에서 가변 분할 방식과 고정 분할 방식의 장단점은 뭔가요?고정 분할 방식 (Fixed Partitioning)장점:같은 크기로 나누어 관리가 쉽고 구현이 간단하며 오버헤드가 적음단점:내부 단편화 발생 → 프로세스가 할당된 파티션보다 작으면 공간 낭비파티션 크기를 적절히 설정해야 하므로 유연성이 부족함가변 분할 방식 (Variable Partitioning)장점:내부 단편화 문제 해결 → 필요한 만큼만 메모리를 할당하므로 공간 낭비가 적음파티션 크기를 유연하게 조정 가능해 고정 분할 방식보다 유연한 운영 가능단점:외부 단편화 발생 → 작은 빈 공간이 여기저기 생겨 전체 메모리를 충분히 활용하기 어려움CPU 사용률을 올리기 위해 멀티프로그래밍을 올렸지만 스왑이 더 많이 이루어져 CPU 사용률이 0%에 가까워 지는 것을 뭐라고 할까요?스레싱멀티프로그래밍을 과도하게 증가시켜 페이지 부재 증가프로세스가 실제 작업을 수행하지 못하고 페이지 교체 작업에만 CPU 자원을 소비HDD나 SSD는 컴퓨터를 실행시키는데 꼭 필요한 걸까요?이유를 함께 적어주세요. 실행 시키기엔 필수가 아니지만 휘발성 메모리를 실행하고 종료할때 데이터가 날아가기 때문에 저장할 수 있는 공간인 HDD/SSD는 꼭 필요하다고 생각함.파일을 삭제해도 포렌식으로 파일을 복구할 수 있는 이유가 무엇일까요?파일을 삭제하면, 운영체제는 파일이 저장된 위치를 빈 공간으로 표시할 뿐, 실제 데이터는 그대로 남아 있어 새로운 데이터가 덮어 쓰기 전에는 복구가 가능함.자료구조와 알고리즘지금까지 배운 5개의 정렬 알고리즘의 장단점과 시간 복잡도를 적어주세요.버블정렬장점: 거의 정렬된 배열에서는 빠르게 정렬 가능 , 구현이 쉬움단점: 큰 데이터는 비효율적시간복잡도: O(n²) (평균/최악) O(n) (최선)선택정렬장점: 추가적인 메모리 사용이 거의 없음단점: 데이터의 크기가 커질수록 성능이 매우 떨어짐시간복잡도: O(n²) (평균/최선/최악)삽입정렬장점: 거의 정렬된 배열에서는 매우 빠름단점: 선택 정렬과 비슷하게 데이터 크기가 커질수록 성능이 떨어짐시간복잡도: O(n²) (평균/최악) O(n) (최선)병합정렬장점: 큰 데이터를 정렬하는 데 적합하고 최악의 경우에도 O(n log n)을 보장단점: 이해와 구현이 어려움. 메모리 공간을 차지함시간복잡도: O(n log n) (평균/최선/최악)퀵정렬장점: 평균적으로 가장 빠른 정렬 알고리즘 중 하나단점: 피벗 선택이 잘못되면 성능이 급격히 떨어짐 (O(n²))시간복잡도: O(n log n) (평균/최선) O(n²) (최악)메모리가 부족한 시스템에서 어떤 문제를 해결하는데 재귀로 쉽게 구현이 가능할 것 같습니다. 여러분이라면 메모이제이션과 타뷸레이션 중 어떤 걸 이용하실 건가요? 이유를 함께 적어주세요. 메모리가 부족한 환경에서는 스택 메모리를 많이 쓰는 재귀보다 반복문 기반의 타뷸레이션이 효율적일 것 같다 생각합니다.

DABBB

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

📖 3주차 학습 내용 요약 지표란 무엇인가우리 제품의 현황과 변화 추이를 알 수 있음지표를 토대로 의사결정을 하고 실행할 수 있음 Proxy 지표가정이 많이 들어간, 간접적인 지표완벽하고 정확한 Proxy에만 가치가 있는 것이 아님 Acquisition(고객 획득)우리는 충분히 많은 신규 고객을 획득하고 있는가?신규 고객을 비용 효율적으로 획득하고 있는가?관련 지표CAC (고객 획득 비용)은 채널별로 확인 필요CLV (고객 생애 가치)는 현실적으로 계산하기 어렵기 때문에대신 Payback Period (비용 회수 기간)이 쓰임Activation(활성화)단순 가입, 로그인 등을 기준으로 삼는 경우가 많지만, 그보다 유저들이 프로덕트의 핵심 가치를 얻는 것을 기준으로 삼자우리 제품의 핵심 가치는 무엇인지, 그 핵심 가치를 더 많은 유저들이 더 빨리 경험하기 위해선?Setup Moment핵심 가치를 경험하기 위한 준비를 마친 순간Aha Moment처음으로 핵심 가치를 경험한 순간Habit Moment핵심 가치를 경험하는 습관을 형성한 순간 Engagement(참여도)얼마나 많은 유저들이 사용하는지DAU, WAU, MAU얼마나 깊이 있게 사용하는지사용한 기능의 수프로덕트 이용 시간얼마나 자주 사용하는지DAU/MAU, DAU/WAU유저들이 프로덕트를 통해 성공적으로 과업을 완수하는지제품마다 유저들이 수행하는 과업이 다르므로 지표도 달라짐 Retention(유지율)Retention is King특정 기간 동안 고객들이 유지되는 비율기간, 분모와 분자 정의해야 함측정 방법Cohort Retention특정 시기에 가입한 유저들이 시간이 경과함에 따라 유지되는 비율Retention Curve코호트 리텐션을 시각적으로 표현한 것Day N RetentionN일째에 유지된 사용자 비율Bracket(Bounded) Retention특정 기간 내에 유지된 사용자 비율Unbounded(On and After) Retention  Event-Based Analytics이벤트를 기반으로 하는 데이터 분석Event유저와 제품 사이 일어나는 상호작용Event Property이벤트에 수반되는 상세 정보Client-side/Server-side Tracking어디에서 데이터를 수집할 것인지에 대한 기준Event Taxonomy 설계Top-Down 접근데이터 활용의 목적에서 시작하여 필요한 데이터를 정의Bottom-Up 접근제품의 주요 이벤트에서 시작하여 필요한 데이터를 정의Naming Convention일관성, 명확성, 이벤트 쪼개는 기준 정하기💬 회고공부할수록 실전에 적용해보고 싶다는 생각이 든다전에 맡았던 제품을 생각해보면, 업력이 오래되었고 우리 서비스를 도입한 시기도 오래된 거래처 / 주 사용자 연령층이 중년 이상인 분들이 대다수였음특성 상 오랜 기간 사용하여 익숙해진 제품을 굳이 이탈하지 않고 사용하는 경우가 많을 것 같음그래서 이탈율이 적어 리텐션이 높은 것이 매출에 영향을 주지 않았을까 라는 추측이 든다 실제 데이터를 통해 기준이 되는 기간과 분모, 분자를 정의해서 지표를 확인해보면 어떤 결과가 나올지 궁금하다..2주차 회고에 궁금했던 내용이 더 자세히 다뤄졌다로그 설계 시 어떤 데이터를 축적할 것인지 정의하고, 그 데이터의 형태와 이름도 정의한다는 내용 🔖 출처강의명 : 시작하는 PM/PO들에게 알려주고 싶은, 프로덕트의 모든 것지식공유자 : 김민우강의 링크 : https://www.inflearn.com/course/시작하는-pmpo-모든것 

기획 · PM· PO

노을

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

자료구조와 알고리즘1. 지금까지 배운 5개의 정렬 알고리즘의 장단점과 시간 복잡도를 적어주세요.버블정렬시간복잡도: O(n²)앞에 있는 숫자와 옆에 있는 숫자를 비교해서 자리를 바꾸는 알고리즘장점: 가장 쉽게 생각할 수 있는 정렬방법, 이해와 구현이 간단단점: 성능이 ON제곱으로 별로 좋지 않음선택정렬시간복잡도: O(n²)배열의 정렬되지 않은 영역의 첫번째 원소를 시작으로 마지막 원소까지 비교 후 가장 작은 값을 첫 번째 원소로 가져옴장점: 이해하기 쉽고 구현하기 쉬움단점: 성능이 좋지않음삽입정렬시간복잡도: O(n²)정렬되지 않은 영역에서 데이터를 하나씩 꺼내서 정렬된 영역 내에 적절한 위치에 삽입하는 알고리즘장점: 이해하기 쉽고 구현하기 쉬움단점: 성능이 좋지 않음병합정렬시간복잡도: O(n log n)재귀로 정렬하는 알고리즘장점: 성능이 훨씬 좋음단점: 재기적인 기법으로 이해하기 어려움퀵정렬시간복잡도: O(n log n)분할정복 알고리즘장점: 성능이 훨씬 좋음단점: 재기적인 기법으로 이해하기 어려움 2. 메모리가 부족한 시스템에서 어떤 문제를 해결하는데 재귀로 쉽게 구현이 가능할 것 같습니다. 여러분이라면 메모이제이션과 타뷸레이션 중 어떤 걸 이용하실 건가요? 이유를 함께 적어주세요.타뷸레이션상향식 계산 방식으로 계산에 필요하지 않을 수도 있는 값도 미리 계산해서 테이블에 저장해 둠, 이렇게 계산되어 저장된 값을 필요할 떄 사용해 빠르게 계산, 메모리도 절약하고 속도도 빠르게 해결할 수 있음   

알고리즘 · 자료구조

제갈진우

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

[인프런 워밍업 클럽 3기] PM/PO 발자국 3주차 회고📌 핵심 요약이번 주는 PM/PO에게 필수적인 데이터 분석 프레임워크를 다루는 강의로, 지표의 본질부터 사용자 여정 기반 분석 구조, 이벤트 기반 데이터 수집 체계까지 전반적인 데이터 전문성을 쌓는 시간이었다. 단순히 ‘데이터를 보는’ 수준을 넘어서, ‘왜 이 지표를 설정하는가’, ‘어떤 지표를 우선순위로 둘 것인가’에 대한 사고방식이 정리됐다.1. 프로덕트 지표 프레임워크 (섹션 5)총 6개 강의를 통해 AARRR 프레임워크 기반의 주요 지표를 구조화하는 방법을 학습했다. 단순히 용어를 외우는 수준이 아니라, 실제 PM 업무에서 어떤 데이터를 왜 봐야 하는지, 무엇을 기준으로 제품 성과를 해석할지에 대해 사고할 수 있도록 설계된 파트였다.[지표의 본질과 Proxy Metric]지표는 측정 가능한 수치일 뿐만 아니라, 제품이 나아갈 방향성을 보여주는 ‘나침반’이다.하지만 실제 비즈니스에서는 매출, 만족도처럼 직접 측정하기 어려운 지표들이 많다. 이때 활용하는 것이 Proxy Metric(대체 지표). 예: ‘고객 만족도’를 직접 측정하기 어렵다면, ‘재구매율’이나 ‘클릭 후 전환율’을 Proxy로 활용 가능.[AARRR 프레임워크 적용]사용자 여정을 기준으로 5단계로 나눠 각 지표를 정리했다.각 단계별로 핵심 지표가 다르고, PM은 각 단계의 문제를 파악하고 지표를 개선하는 과정에 집중해야 한다.Acquisition 어떻게 유입되는가? CAC, CVR, LTVActivation 사용자는 핵심 가치를 경험했는가? Core Action 도달률, Onboarding 완료율Engagement 얼마나 자주 사용하는가? DAU/WAU/MAU, 세션당 평균 행동 수Retention 재방문하는가? N일 잔존율, Cohort 분석Monetization 수익으로 연결되는가? ARPU, 결제전환율 [Metric Hierarchy]조직 단위에서 지표가 흩어지지 않도록 하기 위한 계층 구조.예:최상위: LTV중간: Retention, Conversion하위: DAU, 세션당 페이지뷰 등→ 지표 간 인과관계를 명확히 하고, 무엇을 우선적으로 관리해야 하는지 정리 가능.2. Event-Based Product Analytics (섹션 6)단순 지표 분석을 넘어, 사용자 행동 데이터를 설계하고 수집하는 실무적 시야를 넓힌 파트였다. [Event 기반 분석의 핵심]제품에서 발생하는 모든 사용자의 행동을 이벤트 단위로 쪼개어 수집함으로써, 더 정밀한 분석이 가능하다.예:페이지 방문 → Page View장바구니 담기 → Add to Cart결제 완료 → Purchase 이벤트에는 기본 정보 외에도 속성(property)이 붙을 수 있으며, 예를 들어 Add to Cart에 어떤 상품인지, 가격대, 카테고리 등의 정보가 함께 기록된다. [Event Taxonomy 설계의 중요성]이벤트를 무작정 수집하기보다, 명확한 목적과 정의를 기반으로 이벤트 명세서를 설계해야 한다.설계 시 고려 사항:비즈니스 KPI와 연결되는 이벤트 정의마케팅/개발/운영 팀과의 협업을 통한 공통 언어 확보중복/불필요 이벤트 제거로 성능 최적화→ 잘 설계된 Taxonomy는 분석 품질을 결정짓는 핵심. 3. 회고 및 실무 적용 관점이번 강의는 PM이 단순히 지표를 확인하는 역할을 넘어서, 데이터를 기반으로 제품을 성장시키는 설계자여야 한다는 점을 명확히 보여줬다.지표를 "모니터링"하는 데서 그치는 것이 아니라, 문제의 “근본 원인”을 추적하고, 지표 간 상관관계를 통해 해결책을 찾아야 함. 실제 업무에서도 Metric Hierarchy를 정의하고, 팀원들과 공통된 지표 언어를 설정하는 프로젝트를 고민하게 되었다.이번 주 학습을 통해 ‘데이터를 잘 다루는 PM’이란 단순히 SQL을 잘 다루거나, GA 지표를 잘 보는 사람을 말하는 것이 아님을 다시금 확인했다.지표와 이벤트는 제품 전략의 ‘지도와 나침반’이다. PM으로서 이 둘을 해석하고 설계하는 능력이 점점 더 중요해지고 있다.

기획 · PM· PO인프런워밍업클럽PMPO데이터분석프레임워크AARRR

lch9502

워밍업 클럽 3기 BE - 발자국 3주 차

1. 들어가며테스트 코드 강의는 미리 봤었고, 그 때 굉장히 오래 걸렸던 것 같네요. 아마 커리큘럼대로 따라가려면 굉장히 힘들었을 것 같아요...이번 주차 내용은 저한테는 굉장히 도움이 많이 됐었던 내용이였습니다.실제로 참고해서 회사 프로젝트에 테스트 코드를 적용하기도 했었구요.테스트 코드에 대한 막연한 두려움을 없애준 강의라서 정말 듣기 잘했다고 생각한 강의였습니다.  2. 학습했던 내용 나만의 키워드로 작성하기섹션 6. Spring & JPA 기반 테스트Layered Architecture관심사의 분리 때문에 레이어를 분리단위 테스트 VS. 통합 테스트여러 객체가 협력해서 어떤 하나의 기능을 동작한다면 통합테스트가 필요함단위 테스트만으로 커버하기 어려운 영역들이 생기기 때문IoC, DI, AOPORM, 패러다임의 불일치, HibernateSpring Data JPA@SpringBootTest VS. @DataJpaTest VS. @WebMvcTest@SpringBootTest스프링에서 통합 테스트를 위해 제공하는 에노테이션@SpringBootTest 에는 트랜잭션이 달려있지 않음 -> 더 선호@DataJpaTestjpa 관련된 빈들만 주입해서 서버를 띄워주기 때문@WebMvcTest컨트롤러 레이어만 딱 떼서 테스트를 하기 위해 컨트롤러 관련 빈들만 올릴 수 있는 가벼운 테스트 어노테이션@Controller와 @ControllerAdvice 등과 같은 빈만 주입 됨 @Transactional (readOnly = true)읽기 전용으로 하면 CRUD 작업 중에 CUD 작업이 동작하지 않음CQRSCommand 와 Query 를 분리해서 서로 연관이 없게끔 하려는 것@RestControllerAdvice, @ExceptionHandler커스텀 예외를 사용해서 처리하는 것도 자주 쓰이는 방법Spring bean validation@NotNull, @NotEmpty, @NotBlank, ...컨트롤러에서는 최소한의 검증만 하고 도메인 레이어나 서비스 레이어에서 검증할 것들은 따로 처리해서 책임을 잘 분리하기MockMvc MockMvc 란 Mock(가짜) 객체를 사용해 스프링 MVC 동작을 재현할 수 있는 테스트 프레임워크ObjectMapper@MockBean스프링 컨테이너에 mockito 로 만든 Mock 객체를 넣어주는 역할예시로, ProductService 빈에다가 적용을 하면 ProductService Mock 객체를 대신 스프링 컨테이너에 넣어줌  3. 학습 회고너무나 바쁜 한 주를 보냈습니다. 정신이 없었네요.옛날에 정리한 내용과 인강을 2배속으로 다시 봤습니다. 분명 정리를 했는데 처음 듣는 것 같은 내용들이 있어서 당황하긴 했습니다 ㅎㅎ.. 역시 완전히 자신의 것으로 만드려면 수많은 반복이 필요한 것 같네요.이번 기회에 반복할 수 있어서 좋은 기회였다고 생각합니다.특히 반복하면서 옛날에 들을 때는 인지하지 못했던 실무적인 관점이 다시 보이더라구요? 굉장히 의미 있었던 것 같습니다. 4. 미션 회고미션 Day 11사실 이번 미션은 회고하기가 애매합니다.냉정하게 저 스스로에 대한 평가를 하자면 열심히 했다고 보기가 어렵기 때문이죠.스프링 기반이 아니라서 단순히 미션의 기준에 맞춰서 필요하다고 생각한 것만 처리했습니다. (사실 코틀린 스터디를 따로 시작했는데 여기에 에너지를 너무 쏟아서,,,)다음주 라이브 시간에 딴 분들의 코드 리뷰를 보면 깨달음과 반성을 동시에 할 것만 같네요.. 😅😅😅

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

김보민

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

워밍업 클럽도 벌써 3주차가 되었다. ✅ 강의3주차에는 어드민 개발을 마무리 짓는 내용이었다.공통 Exception과 Advice를 개발하고 적용했다.각 CRUD 기능을 개발하였다.나머지 화면단은 템플릿 라이센스 이슈로 조금의 문제가 있어서 이번에는 넘어가게되었다. ✅ 미션3주차는 [미션5] 삽입, 수정, 삭제 REST API 만들기가 메인이었다.미션의 조건은 플레이리스트 삽입, 수정, 삭제로 잡았지만,초기에 계획했던 API는 아래와 같아서 미션3에서 완성했던 음원 목록 조회쪽 제외 나머지 기능을 완성하는 것을 목표로 했다.구현하면서 조금 헤맸던 포인트는 두가지 정도가 있었다. 그 포인트들에 대해 기록하면서 진행했는데 발자국에도 조금 소개해보려고 한다. (강의에서 나왔던 내용이기도 하다..😅) 플레이리스트 생성 시 연관관계 생기지 않는 문제 fun createPlaylist(request: CreatePlaylistRequest): Playlist { val host = memberRepository.findById(request.hostId) .orElseThrow { IllegalArgumentException("해당 멤버가 존재하지 않습니다.") } val playlist = Playlist( member = host, title = request.title, playlistImage = request.playlistImage ) return playlistRepository.save(playlist) } 처음에 만든 플레이리스트 생성 코드이다. Playlist를 생성할 때, TrackPlaylist 데이터를 생성해주는 로직 자체가 없다.JPA의 연관관계 설정은 엔티티 간 관계만 정의할 뿐, 연관된 데이터를 자동으로 생성해 주진 않는다. 반드시 직접 TrackPlaylist 객체를 생성하고 저장해 줘야 한다. @Transactional fun createPlaylist(request: CreatePlaylistRequest): Playlist { val host = memberRepository.findById(request.hostId) .orElseThrow { IllegalArgumentException("해당 멤버가 존재하지 않습니다.") } val playlist = Playlist( member = host, title = request.title, playlistImage = request.playlistImage ) playlistRepository.save(playlist) request.trackIds.forEach { trackId -> val track = trackRepository.findById(trackId) .orElseThrow { throw BadRequestCustomException("해당 음원 트랙이 존재하지 않습니다.") } val trackPlaylist = TrackPlaylist( track = track, playlist = playlist ) playlist.addTrackPlaylist(trackPlaylist) } return playlist } 수정된 코드이다. 추가로 Playlist 엔티티에도 설정이 필요하다.CascadeType.ALL과 orphanRemoval=true 옵션을 추가해줬으므로, 편의 메서드를 호출하고 Playlist를 저장할 때 TrackPlaylist까지 자동으로 영속화하도록 했다.orphanRemoval default는 false이다.부모 엔티티에서 자식 엔티티가 제거된 경우, 이 자식 엔티티를 더 이상 참조하는 곳이 없어지면 JPA가 자동으로 자식 엔티티를 DB에서 삭제해주는 옵션이다.즉, 부모와의 연결이 끊어지면 자식 데이터가 자동으로 삭제되는 것이다.  플레이리스트 상세 조회시 오류playlist를 id기반으로 가져오려고 했는데 오류가 발생했다. JPA lazy 로딩 이슈였다.현재 Playlist 엔티티에서 host(Member)와 trackPlaylists 필드가 모두 FetchType.LAZY로 설정되어 있다.이로 인해, 영속성 컨텍스트가 종료된 이후에 프록시 객체를 접근하려고 하면 오류가 발생한다.@ManyToOne(targetEntity = Member::class, fetch = FetchType.LAZY) var host: Member = member @OneToMany(mappedBy = "playlist", fetch = FetchType.LAZY) var trackPlaylists: MutableList<TrackPlaylist> = mutableListOf()Fetch Join을 사용해야한다. PlaylistRepository에 다음과 같은 메서드를 추가하여 Fetch Join으로 데이터를 즉시 로딩하게 한다.interface PlaylistRepository : JpaRepository<Playlist, Long> { @Query("select p from Playlist p join fetch p.host left join fetch p.trackPlaylists tp left join fetch tp.track where p.id = :id") fun findByIdWithDetails(@Param("id") id: Long): Playlist? } fun getPlaylist(playlistId: Long): Playlist { return playlistRepository.findByIdWithDetails(playlistId) ?: throw BadRequestCustomException("해당 플레이리스트가 존재하지 않습니다.") }  🍀 마무리미니 프로젝트도 거의 마무리가 되었다. 사실 기능 구현보다 테스트 코드 작성이 생각보다 훨씬 어렵게 느껴진다. 익숙치 않아서 그런가....뭔가 따로 테스트 코드에 대해서 학습이 필요할 것 같다. 그래도 미니 프로젝트를 통해 어떤 방식으로 구현하고 테스트를 작성해야할지 아주 조금...감이 오기 시작했다. 이제 다음주면 마지막 주차이다. 마지막 주도 잘 마무리해서 유종의 미를 거뒀으면 좋겠다. 화이팅!!! 

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

1. 학습 내용 <프로덕트 지표 프레임워크>-지표: 우리 사업, 제품의 현황과 성과를 측정해서 정량화한 것상시 모니터링하는 지표와 그 때 그 때 확인하는 지표로 나뉨 -상시 모니터링->제품의 현황, 추이 변화 알 수 있음. 지표 토대로 의사결정과 실행 가능신규 유저 획득(Acquistion)획득한 유저 활성화(Activation)유저 인게이지먼트(Engagement)지속적인 이용(Retention)수익화(Monetiazion) -그 때 그 때 확인하는 지표Feature AdoptionFeature Retention세그먼트 지표 변화 -지표 설정 프레임워크 1. Proxy 지표에 대해가정이 많이 들어간, 간접적인 지표-> Proxy Metric지표 설정, Science이면서 Art일단 가능한 수준에서 정의완벽한 정확성과 엄밀함이 중요하지 않음.팀이 집중해서 성과를 개선하는 것이 중요함. -지표 설정 프레임워크 2. Acquisition질문 1. 우리는 유저/고객을 충분히 많이 획득하고 있나?질문 2. 비용 효율적으로 획득하고 있나?고객 획득 비용(CAC): 채널별 확인고객 생애 가치(CLV, CLTV, LTV)투자 회수 기간(Payback Period) -지표 설정 프레임워크 3. ActivationActivation: 신규 획득한 사용자들이 프로덕트의 핵심 가치를 경험하는 습관을 형성하는 것1) Setup Moment2) Aha Moment3) Habit Moment -지표 설정 프레임워크 4. EngagementEngagement: 사용자들이 프로덕트에 관심을 갖고, 이용하고, 관계 맺는 것질문 1. 얼마나 많은 유저들이 이용하나? (Breadth): DAU, WAU, MAU질문 2. 제품을 얼마나 깊이 있게 이용하나? (Depth): 액티브 유저 중 n가지 이상의 기능을 이용한 사용자 수, 비율시간을 기준으로도 설정 가능: Time Spent / DAU, Time Spent / WAU, Time Spent / Session질문 3. 얼마나 자주 지용하나? (Frequency): DAU/MAU or DAU / WAU -> 이용 빈도 평균을 보여주는 지표Lness질문 4. 성공적으로 과업을 완수하나? (Efficiency) -지표 설정 프레임워크 5. RetentionRetention: 고객이 제품을 계속해서 이용하는 것Retention Rate: 특정 기간 동안 고객들이 유지되는 비율 -> 단위는 사용자들의 제품 이용 주기에 따라 적절한 시간 단위를 정해야 함리텐션을 측정하고 보는 방식: 코호트 리텐션, 리텐션 커브, Day N Retention, Bracket Retention or Bounded Retention, Unbounded Retention or On and After Retention -지표 설정 프레임워크 6. Monetiazion매출: ARPU, ARPPU기간별 매출 성장률(전주 대비, 전월 대비)Paying Users 수Net Revenue(Dollar) Retention <Product Analytics 주요 개념들>데이터는 투자가 필요한 자원Event-Based AnalyticsEvent, PropertyClient-side / Server-side Tracking <Event Taxonomy 설계 방법>어떤 이벤트, 어떤 프로퍼티를 트래킹하지 구체적으로 정의하는 방법 = Event Tracking Plan 만들기Top-down 접근: 목적을 중시, 더 나은 방법Bottom-Up 접근 Naming Convention: 일관성, 명확성 등-> 사후 관리에서 중요 회고딘계별로 지표 설정을 하는 과정을 배울 수 있었다. 프로덕트 매니저로서 데이터에 관심을 갖고 활용할 줄 알아야 한다는 것을 알고 있었지만, 어느 정도로 어떻게 데이터에 다가가야하는지는 잘 모르고 있었는데 이번 기회에 알 수 있어 좋았다. 데이터는 투자가 필요한 자원이라는 말에 공감이 되었다. 한 편으로 지표 설정이 Science이면서 Art라는 점이 새로운 관점이라는 생각이 들었는데, 지표 설정이 실제로 프로젝트를 만들고 성장시켜나갈 때 고민이 많이 필요한 지점이라는 생각이 들었다.

기획 · PM· POPMPO프로덕트매니저김민우

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

메모리의 종류는 어떤것들이 있나요? 각 메모리의 특징도 함께 적어주세요.레지스터 - 가장 빠르고 CPU 명령 수행시 사용캐시 메모리 - 메모리와 레지스터 중간에서 데이터를 임시 저장함.메모리 - 프로그램 실행 시 데이터를 올려두는 공간보조 기억장치 - 비휘발성으로 데이터를 영구 저장 사용자 프로세스가 메모리의 운영체제 영역에 침범하지 못하도록 만든 레지스터는 어떤 레지스터일까요?경계 레지스터사용자 프로세스가 접근할 수 있는 메모리 범위를 지정하여 경계를 넘을 경우 차단하여 OS의 영역을 보호 메모리 할당 방식에서 가변 분할 방식과 고정 분할 방식의 장단점은 뭔가요?가변 분할 방식 : 프로세스 크기에 맞춰 메모리를 할당하여 메모리 낭비가 적지만, 외부 단편화가 발생하여 조각 모음을 실행해야 함고정 분할 방식 : 조각 모음이 불필요하지만 내부 단편화 발생하여 메모리가 낭비되고 프로세스 크기가 제약된다. CPU 사용률을 올리기 위해 멀티프로그래밍을 올렸지만 스왑이 더 많이 이루어져 CPU 사용률이 0%에 가까워 지는 것을 뭐라고 할까요?스레싱 : 메모리가 부족하여 페이지 교체가 빈번히 일어나 스왑 작업에 더 많은 작업이 실행됨. HDD나 SSD는 컴퓨터를 실행시키는데 꼭 필요한 걸까요?반드시 필수는 아니지만 OS가 저장되거나 사용자 데이터를 비휘발성으로 저장되어야 하기 때문에 필요하다고 생각됨 파일을 삭제해도 포렌식으로 파일을 복구할 수 있는 이유가 무엇일까요?파일시스템에서 파일로 해당 포인터만 제거하고 실제 데이터 블록은 남아있게 됨.

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

지금까지 배운 5개의 정렬 알고리즘의 장단점과 시간 복잡도를 적어주세요.버블정렬,선택정렬,삽입정렬,병합정렬,퀵정렬이 있습니다.. 버블정렬성능 : O(n²)의 성능을 가집니다장점 : 구현이 쉽고 단순합니다.단점 : 성능이 좋지않아 대규모 데이터 처리에 좋지않습니다. 선택정렬성능 : O(n²)의 성능을 가집니다장점 : 구현이 쉽고 단순합니다. (버블정렬과 동일)단점 : 성능이 좋지않아 대규모 데이터 처리에 좋지않습니다. (버블정렬과 동일) 삽입정렬성능 : O(n²)의 성능을 가집니다장점 : 구현이 쉽고 단순합니다. (버블정렬과 동일)단점 : 성능이 좋지않아 대규모 데이터 처리에 좋지않습니다. (버블정렬과 동일) 병합정렬성능 : O(n log n)의 성능을 가집니다.장점 : 안정적인 성능을 보여줌단점 : 구현과 이해가 어렵고 메모리 공간을 많이 차지 퀵정렬성능 : 평균적으로 O(n log n)의 성능을 가지고 최악의 경우 피벗이 한쪽으로 치우쳐 O(n²)를 가지지만 보통 최악의경우가 나올 확률은 극히 낮아 좋은 피벗을 선택해 평균적인 성능으로 말해 O(n log n)의 성능을 가집니다.장점 : 속도가 빠르고 추가 메모리 사용이 거의 없음단점 : 최악의 경우 O(n²)의 성능을 가짐   메모리가 부족한 시스템에서 어떤 문제를 해결하는데 재귀로 쉽게 구현이 가능할 것 같습니다. 여러분이라면 메모이제이션과 타뷸레이션 중 어떤 걸 이용하실 건가요? 이유를 함께 적어주세요.재귀로 쉽게 구현할 수 있는 문제라면 메모이제이션을 사용하는 것이 좋습니다메모이제이션은 재귀 기반 방식이라 코드가 직관적이고 분할하여 해결하기도 쉬워 구현이 간단합니다.반면 타뷸레이션은 반복문 기반으로 계산을 순서대로 처리해 안정적이지만 코드가 길어질 수 있습니다.  타뷸레이션과 메모이제이션을 실행했을때 public static int fibonacci1(int n){ // 타뷸레이션 if(n <= 1){ return n; } int[] table = new int[n+1]; table[0] = 0; table[1] = 1; for(int i = 2;i <= n;i++){ table[i] = table[i - 2] + table[i - 1]; } return table[n]; } public static int fibonacci2(int n,HashMap<Integer,Integer> memo){ // 메모이제이션 if(n == 0 || n == 1){ return n; } if (!memo.containsKey(n)) { memo.put(n, fibonacci2(n - 2, memo) + fibonacci2(n - 1, memo)); } return memo.get(n); } public static void main(String[] args) { HashMap<Integer,Integer> memo = new HashMap<>(); System.out.println("메모이제이션 : " + fibonacci2(5,memo)); System.out.println("타뷸레이션 : " + fibonacci1(5)); }        

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

메모리의 종류는 어떤것들이 있나요? 각 메모리의 특징도 함께 적어주세요.레지스터 : 가장 빠른 기억 장소 이고 컴퓨터의 전원이 꺼지면 데이터가 사라져 휘발성 메모리입니다.캐시 : 레지스터와 메인 메모리 사이에 위치해 데이터 접근 속도를 향상시켜주고 cpu가 메인메모리의 데이터를 가져오기전에 L1 -> L2 -> L3 순으로 확인을하고 캐시에 데이터가 없으면 메인 메모리에서 데이터를 가져옴메인메모리 : 운영체제와 프로세스가 사용하는 공간으로 역시 전원이 꺼지면 데이터가 사라지는 휘발성 메모리입니다하드디스크 : 전원이 종료되도 데이터가 유지되는 비휘발성 메모리에 비용이 저렴하지만 속도가느려 보통 실행할 프로그램을 저장하는데 쓰임 사용자 프로세스가 메모리의 운영체제 영역에 침범하지 못하도록 만든 레지스터는 어떤 레지스터일까요?base address와 bound address 입니다.  메모리 할당 방식에서 가변 분할 방식과 고정 분할 방식의 장단점은 뭔가요?가변 분할 방식장점 : 코드, 데이터, 스택, 힙 등의 영역을 논리적으로 구분하여 모듈 단위로 관리할 수 있어 영역간 접근 권한 설정이 쉬움단점 : 외부 단편화 발생 고정 분할 방식장점 : 외부 단편화가 발생하지 않고 메모리 관리가 단순함단점 : 내부 단편화 발생   CPU 사용률을 올리기 위해 멀티프로그래밍을 올렸지만 스왑이 더 많이 이루어져 CPU 사용률이 0%에 가까워 지는 것을 뭐라고 할까요?스레싱 입니다물리메모리 용량이 적어서 발생하는거라 메모리 크기를 늘리면 해결되지만 무작정 메모리 크기를 늘려도 현재 프로세스가 동작하는데 문제가없어 스레싱이 발생하지 않으면 크기를 늘려도 문제가 발생하지 않음 HDD나 SSD는 컴퓨터를 실행시키는데 꼭 필요한 걸까요?꼭 필요합니다.보통 컴퓨터 운영체제를 hdd나sdd에 저장하기 떄문에 필요합니다.ram에도 설치가 가능은 하겠지만 ram은 휘발성 메모리이기 때문에 전원이 종료되면 전부 사라져 비휘발성 메모리인 hdd/ssd에 저장을 하는게 비용적인 면이나 안전성면에서 좋습니다  파일을 삭제해도 포렌식으로 파일을 복구할 수 있는 이유가 무엇일까요?파일의 데이터가 완전히 지워지지 않기 떄문입니다.파일시스템은 빈 블록 정보를 모은 Free Block List이 존재하는데 파일 삭제시 파일전부를 지우는게아닌 파일의 헤더만 삭제하고 나머지 블록은 Free Block List에 저장해 두기 떄문입니다.           

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

3주차에는 Admin 개발 실습을 진행했다.Presentation vs. AdminPresentation사용자(방문자)가 보게될 화면에 대한 기능 개발(ex) 홈페이지 메인 페이지, 회원가입/로그인, 계정 정보, 검색 기능 등등 Admin내부 운영진이 보게될 화면에 대한 기능 개발(ex) 사용자 관리, 사용 통계 대시보드, 시큐리티 관리 등등 Q. 굳이 사용 영역을 나눠주는 이유는 무엇일까?A1. 보안 강화: 외부에 노출되면 안되는 데이터(사용자 정보, 매출 등)를 별도로 다루기 위해A2. 개발 및 배포 분리: 각 영역을 독립적으로 관리하기 위해 -> Admin은 내부적으로, Presentation은 외부적으로A3. 권한 분리: 데이터 접근 권한을 달리 하기 위해 -> 운영자 한정으로 고급 권한을 부여 즉, 쉽게 말해 홈페이지와 구별되는 '운영자 전용 작업 페이지'를 만드는 작업이다.Admin 작동 구조크게 보았을 때, 이전에 실습했던 Achievement, Skill 등등 포트폴리오에 들어갈 사항들을 DB 테이블에 맞춰 각자 service 기능을 서술하고 이를 응답했을 때의 예외 처리 기능인 exception이 메인이었다.  작동 방식Presentation과 같다! 아래는 이해를 돕기 위해 이미지. 느낀 점controller, service, exception 등등 이때까지 해오면서 본 과정과 비슷했다.물론 이해하기 어려운 부분이 있었지만 Kotlin을 써보지 못한 낯섬에서 오는 문제라고 생각했다.이번 주에 아쉬운 점이 있다면 강의에서 사용된 뷰 템플릿의 유료화로 강의 진행에 차질이 있었다는 점.물론 강사님의 잘못은 아니지만 기간 내 완강을 하지 못하는 것에 아쉬움이 남는 것은 어쩔수가 없겠다.

워밍업3주차

인프런 워밍업 클럽 스터디 3기 - DS/AL <3주 발자국>

알고리즘재귀(Recursion)어떠한 것을 정의할 때 자기 자신을 참조하는 것. 복잡한 문제를 간단한 하위 문제로 나누어 해결할 수 있다.재귀적으로 정의한 함수를 재귀함수라고 한다.function func(number) {  console.log(number)  return number+1; } func(number)콜스택의 메모리 공간만큼 찬 뒤에 종료된다.기저 조건이 없기 때문에 언제 종료될 지 모른다.재귀 함수는 기저 조건이 반드시 있어야 한다.function func(number) {  if (number >= 11) {    return ; }  console.log(number)  return number+1 }콜스택은 함수가 호출되며 올라가는 메모리 영역이다.콜스택은 filo 구조이다. 함수가 호출 될 때마다 콜스택에 쌓이고 함수가 종료되면 콜 스택에서 제거된다.콜스택의 메모리가 가득 차면 더 이상 쌓일 스택이 없어서 제거된다.재귀적으로 생각하기패턴 1단순히 반복 실행. 이 방법은 콜스택의 공간을 많이 차지 하여 단순 반복문에 비해 좋지 않다.패턴 2하위 문제의 결과를 기반으로 현재 문제를 계산(하향식 계산)하향식 계산은 주로 재귀 함수를 통해 구현가능하다.// 주어진 배열의 합을 구하는 문제 // 부분 배열에 마지막 원소를 더해 답을 도출할 것임 function sumArray(arr) {  if (arr.length == 0) { return null; }  if (arr.length == 1) { return arr[0]; }  return sumArray(arrs.slice(0, -1)) + arr[arr.length - 1] }// 문자열의 길이를 계산하는 문제 function strLength(arr) {  if (arr.length == 0) { return 0; }  return strLength(arr.slice(0, -1)) + 1; }function power(x, n) {  if (n <= 0) { return 1; } return power(x, n - 1) * x; }하노이의 탑재귀 함수의 하향식 계산을 보여줄 수 있는 방법function hanoi(count, from, to, temp) {  if (count == 0) {    return ; } hanoi(count-1, from, temp, to)  console.log(`원판 ${count}를 ${from}에서 ${to}로 이동`)  hanoi(count-1, temp, to, from) } ​ hanoi(3, "A", "C", "B")정렬 - 버블정렬정렬 알고리즘은 여러 가지가 존재한다.버블정렬은 현재 숫자와 비교하는 숫자의 비교하여 위치를 바꾸는 방법이다.현재 숫자가 크다면 비교하는 숫자와 위치를 바꾸어서 제일 마지막에 가장 큰 값을 위치시킨다.이런 식으로 제일 마지막에 있는 숫자는 정렬에 초기화 하지 않고 하나 씩 줄여서 정렬을 계산시킨다.function BubbleSort(arr) {  for (let i =0; i < arr.length; i++) {    for (let j = 0; j < arr.length -i -1; j++) {      if (arr[j] > arr[j + 1]) {        let temp = arr[j]        arr[j] = arr[j+1]        arr[j+1] = temp     }   } } }n을 거칠때마다 마지막의 원소가 하나 씩 정렬된다.O(n^2 / 2)이 되는데 자연수는 제거한다. 따라서 O(n^2)이 된다.장점 : 가장 쉽게 생각할 수 있고 이해와 구현이 간단함.단점 : O(N^2)이므로 성능이 좋지 못하다.정렬-선택정렬선택정렬은 정렬 되지 않은 원소와 가장 작은값을 첫 번 째 원소로 가져온 뒤 그 부분을 치환하고 계속해서 정렬해 나간다.function SelectSort(arr) {  for (let i= 0; i < arr.length - 1; i++) {    let minValueIndex = i    for (let j = i+1; j < arr.length; j++) {      if (arr[j] < arr[minValueIndex]) {        minValueIndex = j     }   }    let temp = arr[i]    arr[i] = arr[minValueIndex]    arr[minValueIndex] = temp } }성능은 버블 정렬과 같이 O(n^2)이 된다.장점 : 이해와 구현이 쉽다.단점 : 성능이 좋지 못하다.정렬 - 삽입정렬삽입정렬은 정렬되지 않은 영역의 가장 앞에 있는 숫자를 하나씩 정렬된 영역에 삽입하며 진행한다.function InsertionSort(arr) {  for (let i = 1; i < arr.length; i++) {    let insertingData = arr[i]    let j    for (j = i - 1; j >= 0; j--) {      if (arr[j] > insertingData) {        arr[j + 1] = arr[j]     } else {        break;     }   }    arr[j + 1] = inseringData } }성능은 선택정렬과 같이 O(n^2)이 된다.장점 : 이해와 구현이 쉽다.단점 : 성능이 좋지 못하다.정렬 - 병합정렬분할정복을 이용한 정렬이다.배열을 반으로 줄이고 이걸 계속 반복하여 1이 나올때까지 분할한 뒤 두 배열을 정렬하여 하나의 배열로 병합하는 것이다.function MergeSort(arr, leftIndex, rightIndex) {  if (leftIndex < rightIndex) {    let midIndex = parseInt((leftIndex + rightIndex) / 2)    MergeSort(arr, leftIndex, midIndex)    MergeSort(arr, midIndex+1, rightIndex)    Merge(arr, leftIndex, midIndex, rightIndex) }   } ​ function Merge(arr, leftIndex, midIndex, rightIndex) {  let leftAreaIndex = leftIndex  let rightAreaIndex = midIndex + 1    let tempArr = []  tempArr.length = rightIndex + 1  tempArr.fill(0, 0, rightIndex+1)    let tempArrIndex = leftIndex  while(leftAreaIndex <= midIndex && rightAreaIndex <= rightIndex) {    if (arr[leftAreaIndex] <= arr[rightAreaIndex]) {      tempArr[tempArrIndex] = arr[leftAreaIndex++]       } else {      tempArr[tempArrIndex] = arr[rightAreaIndex++]   }    tempArrIndex++ }  if(leftAreaIndex > midIndex) {    for (let i = rightAreaIndex; i <= rightIndex; i++) {      tempArr[tempArrIndex++] = arr[i]   } } else {    for (let i = leftAreaIndex; i<= midIndex; i++) {      tempArr[tempArrIndex++] = arr[i]   } }  for (let i = leftIndex; i<= rightIndex; i++) {    arr[i] = tempArr[i] } }장점 : O(nlogn)으로 성능이 좋음단점 : 이해와 구현이 어려움정렬 - 퀵정렬pivot을 설정하여 피봇을 기준으로 정렬하는 정렬방법이다.function quickSort(arr, left, right) {  if (left <= right) {    // 정렬된 pivot의 위치를 정렬 let pivot = devide(arr, left, right)    quickSort(arr, left, pivot - 1)    quickSort(arr, pivot+1, right) } } ​ function devide(arr, left, right) {  let pivot = arr[left]  let leftStartIndex = left + 1  let rightStartIndex = right    while (leftStartIndex <= rightStartIndex) {    while (leftStartIndex <= right && pivot >= arr[leftStartIndex]) {   leftStartIndex+= 1   }    while (rightStartIndex >= left + 1 && pivot <= arr[rightStartIndex]) {      rightStartIndex -= 1   }    if (leftStartIndex <= rightStartIndex) {      swap(arr, leftStartIndex, rightStartIndex)   } }  swap(arr, left, rightStartIndex)  return rightStartIndex } ​ function swap(arr, left, rightStartIndex) {  let temp = arr[left]  arr[left] = arr[rightStartIndex]  arr[rightStartIndex] = temp }퀵 정렬이 더 적은 비교와 더 나은 메모리를 사용하기 때문에 퀵 정렬이 더 좋은 알고리즘으로 평가받는다.동적 프로그래밍 - 메모이제이션분할 정복을 통해 문제를 해결할 경우 저장 장치를 사용하여 시간 복잡도를 많이 줄일 수 있다.// 메모이제이션이 없을 경우 function fibo(n) {  if (n == 0 || n == 1) {    return n }  return fibo(n-1) + fibo(n-2) }위의 코드는 중복으로 계산되는 식이 매우 많다.메모이제이션은 계산결과를 저장하여 사용하는 것이다.해시테이블을 사용하여 메모이제이션을 구현해보자.function fibo(n, memo) {  if (n == 0 || n == 1) return n  if (memo[n] == null) {    memo[n] = fibo(n-2,memo) + fibo(n-1, memo) }  return memo[n] }메모이제이션을 사용하지 않는다면 O(n^2)이고 메모이제이션을 사용하면 O(n)이 소모된다.동적 프로그래밍 - 타뷸레이션타뷸레이션은 상향식 계산방식으로 계산에 필요한 모든 값을 계산 후 테이블에 저장해둔다.function fibo(n) {  if (n == 0 || n == 1) {return n}  let table = [0,1]  for (let i = 2; i <= n; i++) {    table[i] = table[i - 2] + table[i - 1] }  return table[n] }메모이제이션은 공간을 차지하고 콜 스택으로 인한 함수 호출이 걸리지만 타뷸레이션은 콜 스택 메모리를 사용하지 않는다.메모이제이션은 재귀적으로 문제를 하위문제로 나눈다. 어떤 문제의 경우에는 재귀가 직관적이지 않다. 따라서 타뷸레이션을 사용하는 것이 더 좋을 수도 있다.

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

메모리의 종류는 어떤것들이 있나요? 각 메모리의 특징도 함께 적어주세요. 레지스터 - 휘발성 메모리로, cpu 계산할 때 메인메모리에 있는 값을 레지스터로 가져온 후 계산한다. 가장 속도가 빠르고 용량이 작다. 캐시 - 휘발성 메모리로, 메인메모리에 있는 값을 레지스터로 옮기려면 시간이 오래걸리기에 필요할것같은 데이터를 저장한다.단계에 따라 가장 속도가 빠른 L1캐시를 보고, 없다면 L2 캐시 확인, 또 없으면 메인 메모리에서 값을 가져온다. RAM - 휘발성으로, 실제 운영체제와 프로세스들이 올라가는 공간이다. 저장공간 대비 비싸기 때문에 데이터를 저장하기보단 실행중인프로그램만 올린다. HDD,SSD - 비휘발성 메모리로, 앞서 말한 메모리보다 상대적으로 느리다. 하지만 저장할 수 있는 용량이 크고 저장공간 대비 상대적으로 값이 싸다.    사용자 프로세스가 메모리의 운영체제 영역에 침범하지 못하도록 만든 레지스터는 어떤 레지스터일까요? base register   메모리 할당 방식에서 가변 분할 방식과 고정 분할 방식의 장단점은 뭔가요? 가변 분할 방식은 원하는 논리영역만큼 할당하여 필요한 만큼만 메모리 영역을 사용할 수 있다. 이를 통해 다른 프로세스와 세그먼트를 공유하기 편하고, 각 영역에 댛나 메모리 접근 보호도 편하다.하지만 사용 후 회수할 때 외부단편화가 일어나서 메모리 낭비가 심하다. 고정 분할 방식은 메모리 영역을 미리 고정된 크기로 나누기 때문에 오버헤드가 적다.하지만 작은 프로세스도 큰 영역에 할당되기 때문에 내부단편화가 발생한다.   CPU 사용률을 올리기 위해 멀티프로그래밍을 올렸지만 스왑이 더 많이 이루어져 CPU 사용률이 0%에 가까워 지는 것을 뭐라고 할까요? 스레싱   HDD나 SSD는 컴퓨터를 실행시키는데 꼭 필요한 걸까요? 단지 데이터를 저장하는 장치일 뿐이기 때문에 실행에 꼭 필요하다고 생각하지 않는다.그 외의 메모리 안에 운영체제, 실행시킬 파일의 데이터가 모두 올라간다면 문제되지 않는다. 하지만 HDD나 SSD를 제외한 저장장치는 휘발성 메모리이기 때문에, 운영체제를 저장해둘 비휘발성 메모리가 필요하다.    파일을 삭제해도 포렌식으로 파일을 복구할 수 있는 이유가 무엇일까요? 특정 파일을 삭제해도 파일 시스템은 파일의 모든 정보를 지우는 것이 아니라, 파일 테이블의 헤더만 삭제하고 free block list에 추가하기때문에 사용자 입장에선 사라진것처럼 보이지만사용했던 블록의 데이터는 그대로 남아있기 때문에 복구할 수 있다.  

이희은

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

3주차 학습1) 피드백 & 네비게이션 컴포넌트 만들기 피드백 컴포넌트와 네비게이션 컴포넌트를 배웠습니다. 네비게이션 컴포넌트에서 기존에 만들었던 컴포넌트를 가져와 변형하는 방식이 재미있었고, 피드백 컴포넌트에서 모달 컴포넌트 만드는게 가장 기억에 남습니다. nested instances를 다 추가하다보면 길어지는 경우가 많아 적절히 사용해야할 것 같습니다2) 다크모드 & 브랜드 모드 다크모드와 브랜드 모드를 만드는 방법에 대해 배웠습니다. 브랜드 모드를 바꿀 때 컴포넌트 색상이 촤라락 바뀌는 부분이 신기하고 쾌감이 느껴졌습니다. 다크모드 색상을 정할 때 웹 접근성을 유의하며 정해야할 것 같습니다. 따라하기만 해서 다시한번 강의를 보면서 볼드님이 설정한 색상들을 한번 정리해봐야겠습니다. 회고좋았던 점&잘한 점: 이전에 밀린 부분이 있어서 이번주에 진도를 거의 따라가려고 노력했다. 컴포넌트 부분이 많이 어려웠는데 포기하지않고 강의들은 후 혼자 만들어보는 방식으로 익힐려고 노력 했다. 어려웠던 점 : 컴포넌트 부분이 많이 어려워서 다시 강의를 듣고 용어같은건 정리해서 틈틈히 외워야할 것같다 다음주 다짐1) 컴포넌트 배운거 용어들 복습하기 2) 진도 잘 따라가기

인프런프로덕트디자이너프로덕트디자인피그마베리어블

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

회고 - 미션https://shore-judge-641.notion.site/Day7-1b34d1a0b2288042ad1fc21d2003a598?pvs=4아무래도 이번 일주일 중 가장 기억에 남는 것은 리팩토링 실습 미션이다. 코드 리뷰를 받는 거기도 해서 최대한 잘 해보고 싶다는 생각에 열심히 하다보니 하루를 통째로 써서 리팩토링을 하게 됐다.강의를 들을 때 강의 앞부분에서 어떤 내용을 가지고 리팩토링을 할지에 대해 설명을 들으면 혼자 한 번 생각나는 대로 코드도 좀 고쳐보고 그 다음에 강의를 듣던 식으로 해왔다. 그래서 그런식으로 생각나는 대로 하면 될 거 같았는데, 막상 혼자 해보려니까 쉽지 않았다.코드를 보면 뭐가 잘못 되긴 했구나라는 생각이 들고, 좀 더 보다보면 이게 문제니까 어떤 식으로 고쳐야겠다는 생각은 든다. 하지만 이 다음부터 고치려하면 어떻게 곷쳐나가야 하는지가 좀 막막했다.그럴때마다 하나의 리팩토링 과정에서는 하나만 진행해야 한다는 점을 마음속으로 되새기면서 하나씩 고쳐나가 보았다.리팩토링을 진행하면서 현재 고치고 있는 부분외에도 거슬리는 부분이 들어와 다른 거 막 고치고 싶은 생각이 들때도 있었는데 그럴때마다 일단 지금 하는 것부터 고치자며 마음을 다 잡고 진행했다.그리고 고치려는 게 너무 크게 느껴지면 조금씩 쪼개서 해보려고도 했다.리팩토링에서 뿐만 아니라 뭔가를 구현해야 할 때 문제를 풀어야 할 때 등 개발을 배우고 나서 많은 상황에서 뭔가를 한번에 하려고 하면 안되고 되는 것부터, 할 수 있겠다고 생각 드는 것부터 해나가야 되는구나를 여러번 느끼게 되는 거 같다.  중간 점검 라이브코드리뷰계속 원래 의도와 다르게 동작해서 나를 힘들게 해서 그 부분이 있어서 이에 대해 질문드렸었다.똑같은 내용을 복사해놓고 리팩토링을 진행하다보니 똑같은 이름의 다른 폴더에 있는 파일을 import를 해버려서 생긴 문제였다... 혹시 다른 폴더에 있는 걸 import하지 않았는지 확인을 하긴 했었는데 모든 파일을 확인했던게 아니라서 참..실전에서는 같은 프로젝트내에 같은 이름을 갖는 파일을 만들 것 같지는 않으니 괜찮을 거 같긴 하지만 가장 기억에 남는다..ㅎㅎ 코드리뷰를 보면서 다른 분들의 코드를 보고 다른 분들의 생각, 사고의 흐름을 볼 수 있었다.이때 코드 리뷰를 보면서 열린 마음으로 다른 사람의 코드를 볼 수 있어야 겠구나라는 생각을 했다. 우빈님은 본인의 원래 생각과 다르게 리팩토링을 한 코드를 보면서 충분한 이유가 있지 않을까 생각해보고 납득할 만한 이유가 있다는 생각이 들면 ok하셨다.뭐 나도 다른 분들 코드를 보면서 틀렸다고 생각한 적이 있는 건 아니지만, 아무래도 내가 리팩토링하면서 코드를 작성할 때 다 나름의 고민을 하고 그 고민의 결론에 맞게 코드를 짰기 때문에 나와 다른 결론을 가지고 짠 코드를 보면 바로 받아들여지지는 않는 것 같다. 나와 다른 코드를 받아들이기 위해서는 한 단계 받아들이기 위한 사고를 거쳐야 한달까?예를 들어 나는 A와 B중에 뭐가 더 나을까 고민해서 "음.. A가 좀 더 나은 듯!" 해서 A를 선택해서 코드를 작성했다면, B를 선택한 코드를 보면 바로 받아들여지기 보다는 "B보다는 A가 좀 더 낫다고 생각했는데?"라고 한 번 생각하고 나서 "음 다시 생각해보니까 A나 B나 뭐를 선택하든 뭐 비슷한가? 이 선택은 뭐를 확실히 더 낫다고 말하기는 애매한가?"라는 생각을 거쳐야 비로소 "B도 나쁘지 않군!", "B도 이렇게 하니까 괜찮네?"라는 결론을 내릴 수 있달까?의도하지 않으면 나와 다른 코드를 쉽게 배척해버릴 수도 있겠다는 생각을 해서 열린 마음이 필요하다는 생각을 하게 됐다.그리고 무엇보다 내가 고민조차 해보지 못한 지점에서 고민을 한 다른 분들의 코드를 보면서 저런 고민도 해볼 수 있겠다라는 생각을 했다.  강의내용 Readable Code강의 중에서 Minesweeper의 필드였던 gameStatus를 GameBoard의 필드로 옮긴 내용이 있었다.나는 처음에 이 부분이 바로 받아들여지지는 않았다. gameStatus라는 것은 말 그대로 Game에 대한 것이지 Board에 대한것이 아니라고 생각했기 때문이다. 그래서 어떤 근거로 gameStatus가 GameBoard의 필드가 될 수 있는 지에 대해 정리해 보았다.gameStatus의 상태를 바꾸거나 확인하는 것은 Minesweeper에 있었지만 이건 단지 현재 gameStatus가 Minesweeper의 필드로 있어서 그런 것일 뿐이다. gameStauts가 바뀌는 것은 GameBoard에서 선택된 셀에 어떤 act(flag 또는 open)를 할때이다. GameBoard에서 어떤 셀에 act를 할때 그대로 이어서 GameBoard에서 gameStatus를 바꾸든가 확인을 해야지 그걸 굳이 GameBoard의 메서드를 호출한 Minesweeper에게 다시 gameStatus에 대해 조작이나 확인을 부탁하는 것이 부자연스럽다. Minesweeper라는 이름때문에 게임자체를 의미하는 것 같지만 Minesweeper의 역할은 외부세계와의 접점이 되는 controller의 역할이다. 게임의 핵심적인 로직은 모두 GameBoard에 들어가있다. 그러니까 내가 생각한 Game 그 차제를 의미하는 거고 Minesweeper는 Game을 운영하는 것 뿐이다. 그래서 Game의 현재 이기고 지는 상태를 의미하는 gameStatus도 자연스레 GameBoard의 필드가 되어야 한다.  DFS를 재귀로 구현했을 때의 stack overflow의 위험이 있다. -> 재귀 구현은 조심해서 해야 됨DFS가 안되면 BFS로 해야하나 싶긴 했는 데 둘 다 시간 공간 복잡도는 비슷한 걸로 기억한다.DFS를 메서드 재귀호출이 아닌 스택을 활용해서 구현해서 stack overflow를 방지한다. -> 스택을 활용해서 DFS를 구현할 수도 있다는 것을 배웠다. Practical Testing테스트를 통해 원하는 또는 필요한 기능을 정의한다. -> 테스트가 스펙이 된다. -> 테스트는 문서다.어떤 기능을 어떻게 사용하고 어떤 케이스에 어떤 결과가 나오는지에 대해 먼저 생각한다. -> 그렇다면 테스트를 먼저 작성해서 이로부터 기능을 구현해볼 수 있다. (TDD) 

백엔드

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

회고3주차에도 많은 것을 학습하였습니다. 테스트 코드 적용 실습과 함께 Persistence Layer, Business Layer, Presentation Layer 계층마다의 특징과 테스트코드 작성법을 직접 적용해보는 점이 좋았습니다. 마지막 주차도 열심히 수강하여 완강을 목표로 해보겠습니다.!강의 내용 요약테스트 코드가 필요한 이유시간 절약: 수동 테스트보다 빠르고 자동화 가능안정성: 휴먼 에러 방지, 신뢰성 있는 코드인수인계 용이: 명확한 테스트가 곧 문서 역할단위 테스트작은 코드 단위를 외부 의존 없이 테스트로 빠르고 안정예외 케이스, 경계값 등을 세분화해서 테스트 단위 테스트 작성할 때 주의점암묵적이거 아직 드러나지 않은 요구사항이 있는가? 에 대한 의문을 가지며 테스트 케이스를 세분화하자해피케이스, 예외케이스에 대해서 고루 케이스를 세분화하자.테스트하기 어려운 값 대해서는 분리하거나 인터페이스화 해서 테스트에 용이한 구조로 만들자 Layered Architecture와 테스트Persistence Layer Test쿼리가 의도대로 동작하는지 검증쿼리를 구현하는 기술이 바뀌어도 기능의 동작을 보장하도록 테스트를 작성, Repository 테스트@DataJpaTest를 사용하면 DB 관련 Bean만 로딩, 테스트 후 자동 rollback(트랜잭션 보장)으로 쿼리 구현 방식이 바뀌어도 동작 보장Business Layer Test비즈니스 로직 흐름과 트랜잭션 처리 검증실제 빈 전체 로딩, 요청값을 생성해 흐름 테스트트랜잭션 rollback으로 데이터 일관성 확인@Transactional readonly=true로 성능최적화 가능 Presentation Layer Test외부 요청, 파라미터 검증 중심의존 관계를 가짜객체를 사용해서 환경을 재현합니다. Service 등은 가짜(Mock)로 주입, 요청-응답 구조, 유효성 검사 테스트에 초점 미션Day 11 - 스터디카페 프로젝트 단위 테스트 작성https://github.com/5jeong/readable-code/tree/main/src/test/java/cleancode

채널톡 아이콘