💸딱 하루, 인프런 천원샵 오픈!

블로그

인프런 워밍업 클럽 스터디 3기 - 백엔드 클린 코드, 테스트 코드 Day 16 미션

레이어 별 특징 및 테스트 방법1. Presentation Layer (프리젠테이션 계층)특징사용자 요청을 받아 비즈니스 계층으로 전달하고, 결과를 클라이언트에게 반환하는 역할.입력 데이터에 대한 기본적인 검증을 수행. Presentation 계층에서의 검증과 Business 계층에서의 검증을 분리해서 생각해야 한다Presentation 계층에서는 보통 형식적인 검증을 한다(예시: 필수 입력 값 검사, 데이터 타입 검사, null 검사, 빈 문자열 검사, 등...)컨트롤러에서 사용하는 요청 DTO가 서비스 계층으로 침투하지 못하도록 컨트롤러 계층에서 서비스 전용 DTO로 변환하는 것을 권장(상황에 따라 다를 수 있다. 만약 받는 포맷이 변할 가능성이 거의 없다면, 그냥 컨트롤러의 DTO를 쭉 사용해도 괜찮을 수 있다.)프론트와의 소통을 위해 RestDocs라이브러리를 사용하기도 한다테스트 방법MockMvc와 같은 도구를 사용하여 HTTP 요청과 응답을 시뮬레이션 (SliceTest)Mockito 같은 프레임워크를 사용하여 Business Layer와 Persistence Layer를 Mocking요청 객체와 응답 객체를 만들고, 관련 동작에 필요한 Service를 모킹해와서 컨트롤러의 동작을 검증@WebMvcTest 어노테이션을 사용하여 Presentation Layer만 로드하여 테스트스프링을 띄우는 비용이 비싸기 때문에 테스트를 위한 스프링 서버를 최소한으로 띄우기도 한다  2. Business Layer (비즈니스 계층)특징핵심 비즈니스 로직을 처리하며, Persistence Layer와의 상호작용을 통해 로직을 수행비즈니스 로직에 따른 도메인 유효성 검사가 이루어진다트랜잭션 관리에 주의해야 한다테스트 방법Persistence Layer를 Mocking하여 단위 테스트를 수행하거나, Persistence Layer와 함께 통합 테스트를 진행테스트와 트랜잭션 관리법1. @BeforeEach와 @AfterEach를 사용하여 테스트 간 격리를 위한 setUp 및 tearDown 메서드를 사용2. @Transactional을 사용하여 트랜잭션을 관리테스트 코드에서 @Transactional을 사용하면 비즈니스 로직이 트랜잭션을 포함하고 있는 것처럼 착각할 수 있으니 주의 3. Persistence Layer (데이터 접근 계층)특징데이터 소스와의 상호작용을 담당, CRUD 작업 수행비즈니스 로직을 포함하지 않아야 한다다양한 데이터베이스 기술들이 사용될 수 있다는 것을 염두 해 두어야 한다(JPA, JDBC, ...)테스트 방법@DataJpaTest를 사용하여 JPA 관련 빈들만 주입하여 가벼운 테스트를 수행 하거나@SpringBootTest를 사용하여 전체 애플리케이션을 로드하여 통합 테스트를 수행할 수 있다.@SpringBootTest는 @Transactional을 포함하지 않으므로 주의 출처 : https://inf.run/EBCNE

[워밍업 클럽 3기 BE code] 미션 day 16

Q."Layered Architecture 구조의 레이어별 테스트 작성법을 알아보았습니다.레이어별로 1) 어떤 특징이 있고, 2) 어떻게 테스트를 하면 좋을지, 자기만의 언어로 다시 한번 정리해 볼까요?" A.Layered Architecture- 레이어 별 관심사를 분리하여 코드의 결합도를 낮추고 유지보수성을 높히는 방식. Persistence layer- 특징 - DB와 통신하는 역할을 수행한다. - 비즈니스 로직에 대한 코드가 아닌 단순 데이터의 입출입을 담당한다.- 테스트 - JpaRepository 중 비즈니스 로직에서 사용하는 메서드는 테스트를 수행하여 향후 변경에 대응한다. - H2 등 테스트를 위한 DB를 활용하여 @SpringDataJpa를 사용한 통합 테스트를 수행한다.  Business layer- 특징 - 비즈니스 로직을 수행한다. - 비즈니스 로직에 따른 세세한 파라미터 검증을 수행한다. - 조회용 로직은 CQRS로 코드를 작성하고 @Transactional(readonly=true)로 처리하자. - 필요에 따라 락을 사용해야할 수도 있다.- 테스트 - @DataJpaTest은 @Transactional이 포함됨을 고려하여 테스트를 수행해야 한다. 기본적으로는 @SpringBootTest을 사용하여 테스트 한다. Presentation layer- 특징 - 클라이언트가 소통하는 레이어. - 최소한의 파라미터 검증을 수행한다. - 테스트 - @WebMvcTest을 활용하여 클라이언트의 요청에 대한 대역을 사용할 수 있다. - 대역 없이 통합 테스트를 수행하기에는 범위가 넓어, 서비스 레이어에 대하여 대역을 고려하여 처리할 수 있다. 이 경우 @MockBean 등을 활용한다. 

Day 16 Mission [Layered Architecture 에 대하여 정리]

1. 레이어 별로 어떤 특징이 있는가?보통 3계층 혹은 4계층으로 구조를 분리하는데 강의에서는 3계층으로 표현하고 있다.프레젠테이션 계층 (Presentation Layer)사용자와 직접 상호작용하는 부분이다. (외부 세계의 요청을 가장 먼저 받는 계층이다.)파라미터에 대한 최소한의 검증을 진행한다.비즈니스 로직 계층 (Business Logic Layer)애플리케이션의 핵심 기능과 규칙을 처리합니다.사용자 요청에 따라 데이터 처리, 검증, 트랜잭션 관리 등을 수행한다.영속성 계층 (Persistence Layer)데이터베이스나 외부 저장소와의 통신을 담당합니다.가공 로직이 포함되어서는 안된다.SQL 쿼리 실행, ORM 매핑 등이 이 계층에서 이뤄집니다.2. 어떻게 테스트 하면 좋을까?각 계층이 다른 계층에 침투하지 않도록 해야한다. 또한 각 레이어의 목표에 맞게 진행해야한다.프레젠테이션 계층 (Presentation Layer)사실 테스트하기 가장 난감하다. 단순 restfull api 서버라면 가능하지만 화면을 제공한다면 테스트가 복잡해진다.화면의 구조나 UI/UX 측인 부분도 검증이 필요하다.restfull 서버라는 가정하에는 MockMvc와 @MockBean을 사용하여 Mocking 처리가 가능하다.비즈니스 로직 계층 (Business Logic Layer)사실상 중간단계의 통합테스트가 많다. @MockBean을 사용하여 Repository를 Mocking하고 비즈니스 로직 검증이 가능하지만 fake객체를 만들어 단독으로 검증할 수 있도록 하는것이 더 좋다.(속도가 월등하다)데이터 접근 계층 (Data Access Layer)@Transactional를 이용한 CRUD 테스트가 가능하다.인메모리 디비를 사용해 테스트가 가능하지만 역시다 fake객체를 이용하여 빠른 속도로 검증이 가능하다.영속성 객체의 속성을 잘못 인지하면 테스트는 통과하지만 실제로는 오류가 발생한다거나 부하테스트 등을 정상적으로 처리다 안되고 영속성 1차 캐시에서만 처리되 무의미한 테스트가 발생할수 있으니 주의가 필요하다.

워밍업 클럽 3기 Code 과정 Day 16 미션

미션 내용Layered Architecture의 레이어별로 1) 어떤 특징이 있고2) 어떻게 테스트를 하면 좋을지자기만의 언어로 정리해보기 Persistence Layer특징DB와 상호작용하는 LayerDB에 값을 넣고 가져오는 로직을 담당비즈니스 가공 로직이 있으면 X (역할과 책임 분리)데이터 CRUD에 집중단위 테스트 느낌 어떻게 테스트 하면 좋을지?사용하는 기술 (ex : jpa, querydsl, jpql)을 내가 잘 사용해서 DB와 상호 작용하는지 테스트기술을 잘못 사용하면 원하는 데이터를 받아올 수 없음jpa의 경우에도 쿼리 메서드를 잘 짜주겠지만 혹시 내가 메서드명을 잘못 지었을 수도 있기에 쿼리 메서드에 대해서도 테스트주로 List 형태로 메서드의 반환 값이 많이 반환될 것이기에 hasSize, extracting, containsExactlyInAnyOrder 메서드 활용하면 좋음데이터를 직접 넣기에 데이터 만드는 메서드가 정의되어 있으면 조금 더 보기 좋음 Business Layer특징비즈니스 로직이 구현된 곳도메인 객체의 로직도 존재트랜잭션 관련된 것에 주의해야 함persistence layer와 상호 작용persistence layer와 business layer 통합 테스트 느낌 어떻게 테스트 하면 좋을지?비즈니스 로직, 도메인 객체의 로직을 수행하는 메서드들에 대해 테스트 진행 (단위 테스트)각 로직들 메서드 단위로 분리를 잘하여 테스트하기 용이하게 해야 함LocalDateTime과 같이 테스트하기 어려운 것들이 존재한다면 분리하고 파라미터로 받게 하여 테스트 용이하게 해야 함도메인과 관련된 validation은 이 layer에서 테스트를 진행하는 것이 더 역할과 책임이 분리된 것역시 데이터 많이 만들기에 데이터 만드는 메서드 있으면 좋음 Presentation Layer특징외부와 상호작용하는 곳api로 파라미터를 받거나 값을 내려주는 곳응답 처리, 예외 처리, validation이 중요한 곳하위 layer들과 분리해서 테스트하는 곳단위 테스트 느낌 어떻게 테스트 하면 좋을지?하위 layer들과 분리해서 테스트하는 것이 좋다. 이를 위해 MockMvc 및 mock 객체 사용andExpect 메서드를 잘 활용해야 함공통 응답이 잘 내려가는지 테스트 필요예외 발생 시 원하는 포맷, 메시지 등이 잘 응답되는지 테스트 필요쿼리 파라미터에 대한 validation 잘 이뤄지는지 테스트 필요여기에서의 validation은 기본적인 @NotNull, @NotBlank 같은 것을 테스트business layer의 validation과 분리하는 것이 필요

아아

인프런 워밍업 클럽 스터디 3기 - 백엔드 클린 코드, 테스트 코드 미션 - Day 16

Layered Architecture는 왜 사용하는 걸까?핵심 개념: 관심사의 분리 (Separation of Concerns)Layered Architecture의 핵심 목적은 관심사의 분리입니다.그렇다면 왜 관심사를 분리해야 할까요?가장 큰 이유는 유지보수성과 확장성 때문입니다.사용자의 요청이 들어왔을 때, 각 Layer가 명확한 책임을 갖고 동작한다면 코드 수정이나 기능 추가가 훨씬 쉬워집니다.Spring Boot에서의 관심사 분리Spring Boot로 개발할 때 우리는 자연스럽게 관심사를 다음과 같이 나누게 됩니다:Controller (Presentation Layer): 사용자의 요청을 받고 응답을 반환Service (Business Layer): 비즈니스 로직 처리Repository (Persistence Layer): 데이터베이스 접근 및 처리아래 예시를 보면서 각 Layer의 역할을 살펴보겠습니다.Presentation Layer사용자의 요청을 받고 요청값을 검증 후 반환합니다. @RestController @RequiredArgsConstructor public class UserController { private final UserService userService; @GetMapping("/login") public LoginResponse login(@RequestBody @Valid LoginRequest loginRequest) { return service.login(loginRequest); } } @Vali를 통해 LoginRequest을 검증합니다.Business Layer비즈니스 로직을 수행하는 핵심 구간입니다. @Service @RequiredArgsConstructor public class UserService { private final UserRespository userRepository; public LoginResponse login(LoginRequest loginRequest) { Users user = userRepository.findByEmail(loginRequest.email()) .orElseThrow( LoginErrorCode.EMAIL_NOT_FOUND::exception ); if(user.matchPassword(bCryptPasswordEncoder, loginRequest.password())) { throw LoginErrorCode.PASSWORD_NOT_FOUND.exception(); } return LoginResponse.builder() .username(user.getUsername) .build(); } Persistence Layer 데이터베이스 접근을 담당합니다. public interface UserRepository extends JpaRepository<Users,Long> { }이렇게 각 레이어가 자신의 역할에 집중하면:- 수정과 확장이 쉬워지고- 테스트 코드 작성도 쉬워지며- 유지보수성이 높아집니다.테스트 코드 작성저는 주로 서비스단을 테스트 코드로 작성해서 검증합니다.서비스쪽이 비즈니스 로직을 담당하고 있는 부분이라 생각하여 제일 중요하다 생각합니다. 그래서 이번에 채팅방 생성하는 테스트 코드를 저의 방식대로 해봤습니다.@ActiveProfiles("test") @SpringBootTest class ChatRoomTest { @Autowired private ChatRoomRepository chatRoomRepository; @Autowired private UserRepository userRepository; @Autowired private BoardRepository boardRepository; @Autowired private ChatService chatService; @DisplayName("방 생성 테스트") @Test void createRoom() { // given Users user = userRepository.findById(1L).orElseThrow(); Product product = boardRepository.findById(1L).orElseThrow(); Users user2 = userRepository.findById(2L).orElseThrow(); Product product2 = boardRepository.findById(2L).orElseThrow(); ChatRoomRequest request = ChatRoomRequest.builder() .userId(user) .productId(product) .name("테스트 채팅방") .build(); ChatRoomRequest request2 = ChatRoomRequest.builder() .userId(user2) .productId(product2) .name("테스트 채팅방2") .build(); List<ChatRoomEntity> chatRoom = ChatRoom.create(List.of(request, request2)); List<ChatRoomEntity> save = chatRoomRepository.saveAll(chatRoom); // when var chatRooms = chatRoomRepository.findAll(); // then assertThat(chatRooms).hasSize(2); assertThat(chatRooms.get(0).getProductId().getId()).isEqualTo(1L); assertThat(chatRooms.get(0).getProductId().getId()).isEqualTo(2L); } } application-test.yml을 만들어 테스트 환경을 구성하였으며 그 구성 기반으로 더미 데이터(data.sql)를 만들어서 테스트를 진행했습니다.

정기석

워밍업 클럽 3기 BE 클린코드&테스트 DAY-18 미션

1. @Mock, @MockBean, @Spy, @SpyBean, @InjectMocks 의 차이MockSpring Context 없이 순수하게 Mock 객체를 만들어서 사용InjectMocksMock은 Spring Context에서 관리해주지 않기 때문에 해당 Mock객체가 필요한 객체에 선언하여 Mock객체를 주입MockBeanSpring Context 에서 관리하는 빈 대신 사용하기 위해 사용MockBean을 사용하면 Srping Context에 있는 빈을 대신해서 사용하기 때문에 Spring에서 의존성 주입을 해준다. Spymock은 가짜객체라서 지정한 행동말고는 아무것도 하지 않지만진짜객체를 기반으로 생성되어 일부 행동에 대해 지정한 값을 반환Mock이 when과 thenReturn을 지정하지만Spy는 진짜 객체를 기반으로 행동하기에 doSometing(return)만 지정실제객체를 기반으로 만들기에 stub과 실제 메소드 혼합해서 테스트 가능SpyBeanmock과 mockbean같은 관계2. 아래 3개의 테스트가 있습니다. 내용을 살펴보고, 각 항목을 @BeforeEach, given절, when절에 배치한다면 어떻게 배치하고 싶으신가요?(@BeforeEach에 올라간 내용은 공통 항목으로 합칠 수 있습니다. ex. 1-1과 2-1을 하나로 합쳐서 @BeforeEach에 배치)@BeforeEach void setUp() { 사용자1 생성에 필요한 내용 준비 사용자1 생성 사용자2 생성에 필요한 내용 준비 사용자2 생성 사용자1의 게시물 생성에 필요한 내용 준비 사용자1의 게시물 생성 } @DisplayName("사용자가 댓글을 작성할 수 있다.") @Test void writeComment() { // given 댓글 생성에 필요한 내용 준비 // when 댓글 생성 // then 검증 } @DisplayName("사용자가 댓글을 수정할 수 있다.") @Test void updateComment() { // given 댓글 생성에 필요한 내용 준비 댓글 생성 // when 댓글 수정 // then 검증 } @DisplayName("자신이 작성한 댓글이 아니면 수정할 수 없다.") @Test void cannotUpdateCommentWhenUserIsNotWriter() { // given 사용자1의 댓글 생성에 필요한 내용 준비 사용자1의 댓글 생성 // when // then 사용자2가 사용자1의 댓글 수정 시도 검증 }

바커스

아이폰 배터리 교체 효율 성능 80이상 만들기 설정 꿀팁

출근길에 배터리가 빠르게 소모되어 고민했던 경험을 바탕으로, 배터리를 오래 사용할 수 있는 설정 및 충전 습관을 정리했습니다.📌 배터리 수명 연장 설정법 (iOS & Android)✅ 1. 화면 밝기 조절자동 밝기보다는 수동 조절이 배터리 절약에 효과적입니다.iPhone: 설정 → 디스플레이 및 밝기 → 자동 밝기 해제Android: 설정 → 디스플레이 → 밝기 수동 조절💡 OLED 디스플레이 기기는 "다크 모드"를 활용하면 더욱 효과적! ✅ 2. 백그라운드 앱 정리불필요한 앱이 계속 실행되면 배터리가 소모됩니다.iPhone: 설정 → 일반 → 백그라운드 앱 새로 고침 → "Wi-Fi 또는 꺼짐"Android: 설정 → 배터리 → 배터리 최적화 → 필요 없는 앱 비활성화💡 SNS, 뉴스 앱 등 필요한 앱만 백그라운드 실행 허용 추천! ✅ 3. 위치 서비스 최적화GPS 사용은 배터리 소모가 크므로 제한하는 것이 좋습니다.iPhone: 설정 → 개인정보 보호 → 위치 서비스 → "앱 사용 중"으로 변경Android: 설정 → 위치 → 사용하지 않는 앱의 위치 액세스 차단 ✅ 4. 배터리 절약 모드 활성화배터리가 부족할 때 절약 모드를 활용하면 사용 시간을 늘릴 수 있습니다.iPhone: 설정 → 배터리 → 저전력 모드Android: 설정 → 배터리 → 절전 모드⚡ 충전 습관 개선 방법 (배터리 수명 연장 핵심!)✅ 1. 20~80% 구간에서 충전하기0%까지 방전 후 충전하는 것은 배터리에 부담을 줍니다.💡 최적의 충전 구간: 20~80%✅ 2. 취침 중 충전 피하기배터리를 장시간 100% 상태로 유지하면 열화가 가속됩니다.💡 "iPhone 최적화된 배터리 충전" 기능 활성화 추천!✅ 3. 고속 충전 남용 금지고속 충전은 발열을 유발하여 배터리 수명을 단축시킬 수 있습니다.💡 일상에서는 일반 충전(5W~15W) 활용!📊 배터리 상태 확인 & 교체 시점 체크iPhone: 설정 → 배터리 → 배터리 성능 상태Android: 설정 → 배터리 → 배터리 사용량💡 배터리 성능이 80% 이하로 떨어지면 교체 고려!🔎 배터리 오래 쓰는 법, 오늘부터 실천하세요!✔ 스마트폰 설정 최적화 📱✔ 올바른 충전 습관 ⚡✔ 배터리 건강 체크 🔋이 방법을 적용하면 배터리 수명이 확실히 길어지는 걸 체감할 수 있습니다. 😊📌 좀 더 상세한 정보가 필요하면 아래 링크를 참조하시기 바랍니다.상세한 정보 보러가기   

교양스마트폰배터리절약법아이온폰배터리수명안드로이드배터리수명아이온폰배터리절약안드로이드배터리설정

[워밍업 클럽 3기 BE 클린코드&테스트] - 레이어드 아키텍처 주요 특징

Spring에서 애플리케이션을 설계할 때 흔히 사용하는 Layered Architecture(레이어드 아키텍처)는 다음과 같은 3가지 주요 계층으로 구성[Presentation Layer] → [Business Layer] → [Persistence Layer] (Controller) (Service) (Repository) Layered Architecture의 핵심 특징계층 분리 : 각 계층이 독립된 책임을 가지며, 역할이 명확히 나뉨독립성 : 상위 계층은 하위 계층을 의존하지만, 하위 계층은 상위 계층을 모름유연성 : 특정 계층을 변경하더라도 다른 계층에 영향이 적음 테스트 용이성 : 계층별 단위 테스트가 가능해 테스트 작성이 명확하고 쉬움1. Presentation Layer (UI 계층 - Controller)✅ 역할사용자 요청을 받아서 적절한 응답을 반환 (주로 JSON 형태)HTTP 관련 처리 (@RestController, @GetMapping 등)✅ 테스트 전략@WebMvcTest를 사용하여 컨트롤러만 테스트MockMvc + ObjectMapper로 HTTP 요청/응답 흐름을 검증Service는 @MockBean으로 주입📌 예시 코드WebMvcTest(controllers = OrderController.class) class OrderControllerTest { @Autowired MockMvc mockMvc; @Autowired ObjectMapper objectMapper; @MockBean OrderService orderService; @Test void 신규_주문_등록_성공() throws Exception { OrderCreateRequest request = OrderCreateRequest.builder() .productNumbers(List.of("001")) .build(); mockMvc.perform(post("/api/v1/orders/new") .contentType(MediaType.APPLICATION_JSON) .content(objectMapper.writeValueAsString(request))) .andExpect(status().isOk()) .andExpect(jsonPath("$.code").value("200")) .andExpect(jsonPath("$.message").value("OK")); } @Test void 상품번호_없을때_예외() throws Exception { OrderCreateRequest request = OrderCreateRequest.builder().build(); mockMvc.perform(post("/api/v1/orders/new") .contentType(MediaType.APPLICATION_JSON) .content(objectMapper.writeValueAsString(request))) .andExpect(status().isBadRequest()) .andExpect(jsonPath("$.message").value("상품 번호 리스트는 필수입니다.")); } } 2. Business Layer (서비스 계층 - Service)✅ 역할비즈니스 로직 처리의 중심트랜잭션 처리, 여러 레포지토리 호출, 외부 API 조합 등✅ 테스트 전략@SpringBootTest 또는 순수 JUnit 테스트로 비즈니스 로직 검증Repository는 필요 시 가짜(Mock) 또는 실제 DB 사용Happy Path + 예외 케이스 + 경계값 등 다양한 시나리오 테스트📌 예시 코드@SpringBootTest class ProductServiceTest { @Autowired private ProductService productService; @Autowired private ProductRepository productRepository; @AfterEach void tearDown() { productRepository.deleteAllInBatch(); ; } @DisplayName("신규 상품을 등록한다. 상품번호는 가장 최근 상품의 상품번호에서 1증가한 것이다.") @Test void createProduct() { //given Product product = createProduct("001", HANDMADE, SELLING, "아메리카노", 4000); productRepository.save(product); ProductCreateServiceRequest request = ProductCreateServiceRequest.builder() .type((HANDMADE)) .sellingStatus((SELLING)) .name("카푸치노") .price(5000) .build(); //when ProductResponse productResponse = productService.createProduct(request); //then assertThat(productResponse) .extracting("productNumber", "type", "sellingStatus", "name", "price") .contains("002", HANDMADE, SELLING, "카푸치노", 5000); List<Product> products = productRepository.findAll(); assertThat(products).hasSize(2) .extracting("productNumber", "type", "sellingStatus", "name", "price") .containsExactlyInAnyOrder( tuple("001", HANDMADE, SELLING, "아메리카노", 4000), tuple("002", HANDMADE, SELLING, "카푸치노", 5000) ); }주의: 테스트에서 @Transactional을 사용할 경우, 실제 코드와 작동 방식이 달라질 수 있음 → 필요에 따라 deleteAllInBatch()로 정리3. Persistence Layer (저장소 계층 - Repository)✅ 역할데이터베이스와 직접 상호작용 (CRUD, 조건 검색 등)JPA, MyBatis 등 ORM/쿼리 도구 사용✅ 테스트 전략@DataJpaTest를 사용하여 가벼운 JPA 테스트테스트 종료 시 자동으로 Rollback 처리됨실제 DB 조회/저장 동작 검증📌 예시 코드@ActiveProfiles("test") //@SpringBootTest @DataJpaTest class ProductRepositoryTest { @Autowired private ProductRepository productRepository; @DisplayName("원하는 판매상태를 가진 상품들을 조회한다.") @Test void findAllBySellingStatusIn() { //given Product product1 = createProduct("001", HANDMADE, SELLING, "아메리카노", 4000); Product product2 = createProduct("002", HANDMADE, HOLD, "카페라떼", 4500); Product product3 = createProduct("003", HANDMADE, STOP_SELLING, "팥빙수", 7000); productRepository.saveAll(List.of(product1, product2, product3)); //when List<Product> products = productRepository.findAllBySellingStatusIn(List.of(SELLING, HOLD)); //then assertThat(products).hasSize(2) .extracting("productNumber", "name", "sellingStatus") .containsExactlyInAnyOrder( tuple("001", "아메리카노", SELLING), tuple("002", "카페라떼", HOLD) ); }요약Controller : HTTP 요청/응답 처리, @WebMvcTest + MockMvc로 요청 파라미터, 상태 코드, JSON 응답 값 등을 테스트Service : 비즈니스 로직 처리, @SpringBootTest or JUnit + Mockito로 로직 분기, 예외 처리, 흐름 제어 등 테스트Repository : 데이터 CRUD 및 조회, @DataJpaTest로 쿼리 정확성, 연관 관계 매핑, 조건 검색 등을 테스트

레이어드 아키텍처 특징 및 테스트 작성법 알아보기

이 글은 박우빈님의 Practical-Testing 강의 를 참조하여 작성한 글입니다. 레이어드 아키텍처(Layered Architecture)란?소프트웨어 시스템을 계층(layer)으로 나누어 구성하는 설계 방식으로, 각 계층이 특정한 역할과 책임을 갖도록 설계하는 것을 의미한다.이렇게 각 계층들을 관심사를 기준으로 분리함으로써 계층의 응집도를 높이고 결합도를 낮출 수 있다.-> 유지보수성이 올라감! 레이어드 아키텍처의 주요 특징 계층 분리 Layered Architecture에서는 보통 3개의 Layer로 구성되어 있다.Presentation Layer (UI 계층): 사용자의 요청 및 응답을 처리하며 상호작용 한다.Business Layer (서비스 계층): 비즈니스 로직을 수행하는 책임을 지닌다.Persistence Layer (비즈니스 계층): DB에 접근하여 상호작용(데이터 CRUD) 한다.   독립성상위 계층은 하위 계층에 의존하지만,하위 계층은 상위 계층에 대한 지식이나 정보를 가지지 않아야 한다.e.g, Presentation Layer에서는 하위 계층인 Business Layer를 의존한다.이때 Business Layer는 상위 계층인 Presentation Layer에서 넘어온 데이터로 비즈니스 로직을 처리할 뿐이며,Presentation Layer 에 대해 알고 있지 않다!유연성 특정 계층의 구현을 변경하더라도 다른 계층에는 영향을 미치지 않도록 설계할 수 있다. 테스트 용이성계층별로 테스트가 가능하므로, 각 계층의 단위 테스트를 독립적으로 수행할 수 있다. 각 계층 테스트 코드 작성법 Presentation LayerAPI의 요청-응답 흐름과 응답 형식(HTTP 상태 코드와 JSON 응답 데이터)을 검증하는 테스트를 작성한다.@WebMvcTest 를 통해 테스트 하고자 하는 컨트롤러를 등록해준다.해당 컨트롤러를 테스트 하기위해 필요한 Business 계층(Service 클래스)를 @MockBean을 통해 주입하며, MockMvc 또한 의존성 주입해준다.이 때 json과의 소통이 필요하므로 객체를 json, json을 객채로 변환할 수 있게끔 ObjectMapper 또한 주입해주자.@WebMvcTest(controllers = OrderController.class) class OrderControllerTest { @Autowired MockMvc mockMvc; @Autowired ObjectMapper objectMapper; @MockBean OrderService orderService; @DisplayName("신규 주문을 등록한다.") @Test void createOrder() throws Exception { // given OrderCreateRequest request = OrderCreateRequest.builder() .productNumbers(List.of("001")) .build(); // when // then mockMvc.perform( post("/api/v1/orders/new") .content(objectMapper.writeValueAsString(request)) .contentType(MediaType.APPLICATION_JSON) ) .andDo(print()) .andExpect(status().isOk()) .andExpect(jsonPath("$.code").value("200")) .andExpect(jsonPath("$.status").value("OK")) .andExpect(jsonPath("$.message").value("OK")); } @DisplayName("신규 주문을 등록할 때 상품번호는 1개 이상이어야 한다.") @Test void createOrderWithEmptyProductNumbers() throws Exception { // given OrderCreateRequest request = OrderCreateRequest.builder() .build(); // when // then mockMvc.perform( post("/api/v1/orders/new") .content(objectMapper.writeValueAsString(request)) .contentType(MediaType.APPLICATION_JSON) ) .andDo(print()) .andExpect(status().isBadRequest()) .andExpect(jsonPath("$.code").value("400")) .andExpect(jsonPath("$.status").value("BAD_REQUEST")) .andExpect(jsonPath("$.message").value("상품 번호 리스트는 필수입니다.")) .andExpect(jsonPath("$.data").isEmpty()); } }  Business Layer작성한 비지니스 로직이 의도한 대로 작동하는지 검증하는 테스트를 작성한다.@SpringBootTest 을 통해 모든 Bean 의존성 주입함으로써 통합 테스트가 가능하다.해피케이스 뿐만 아니라 예외 테스트, 경계값 테스트 등 여러 케이스에 대해 테스트를 작성하자.@Transactional vs sql을 이용한 데이터 삭제실제 코드에서는 @Transactional을 사용하지 않는데, 단순히 롤백만을 위해 테스트코드에서 @Transactional을 사용하면, 실제 작동 방식과 다르게 작동할 수 있다.따라서 테스트 코드 작성시 Transactional의 부작용에 대해 인지하고 사용할 것!!@SpringBootTest class ProductServiceTest { @Autowired private ProductService productService; @Autowired private ProductRepository productRepository; @AfterEach void tearDown() { productRepository.deleteAllInBatch(); } @DisplayName("신규 상품을 등록한다. 상품번호는 가장 최근 상품의 상품번호에서 1 증가한 값이다.") @Test void createProduct() { //given Product product1 = createProduct("001", HANDMADE, SELLING, "아메리카노", 4000); productRepository.save(product1); ProductCreateRequest request = ProductCreateRequest.builder() .type(HANDMADE) .sellingStatus(SELLING) .name("카푸치노") .price(5000) .build(); //when ProductResponse productResponse = productService.createProduct(request); //then assertThat(productResponse) .extracting("productNumber", "type", "sellingStatus", "price", "name") .contains("002", HANDMADE, SELLING, 5000, "카푸치노"); List<Product> products = productRepository.findAll(); assertThat(products).hasSize(2) .extracting("productNumber", "type", "sellingStatus", "price", "name") .containsExactlyInAnyOrder( tuple("001", HANDMADE, SELLING, 4000, "아메리카노"), tuple("002", HANDMADE, SELLING, 5000, "카푸치노") ); } } Persistence Layer 데이터에 접근하는 역할로 데이터 CRUD와 연관된 메서드들을 테스트 한다. @DataJpaTest JPA와 관련된 의존성들만 주입해준다 -> @SpringBootTest보다 가볍다.어노테이션 내부에 @Transactional이 포함되어 있어 테스트 후 데이터가 롤백된다.@DataJpaTest // @DataJpaTest안에 @Transactional이 걸려있어서 자동으로 rollback이 됨 class ProductRepositoryTest { @Autowired private ProductRepository productRepository; @DisplayName("원하는 판매상태를 가진 상품들을 조회한다.") @Test void findAllBySellingStatusIn() { //given Product product1 = createProduct("001", HANDMADE, SELLING, "아메리카노", 4000); Product product2 = createProduct("002", HANDMADE, HOLD, "카페라떼", 4500); Product product3 = createProduct("003", HANDMADE, STOP_SELLING, "팥빙수", 7000); productRepository.saveAll(List.of(product1, product2, product3)); //when List<Product> products = productRepository.findAllBySellingStatusIn(List.of(SELLING, HOLD)); //then assertThat(products).hasSize(2) .extracting("productNumber", "name", "sellingStatus") .containsExactlyInAnyOrder( tuple("001", "아메리카노", SELLING), tuple("002", "카페라떼", HOLD) ); } }Referencehttps://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 

백엔드레이어드아키텍쳐워밍업클럽테스트코드

별안간뿌우

[인프런 워밍업 클럽_3기 백엔드 프로젝트]2번째 발자국

지금부터는 실습 위주라 발자국을 자세하게 남기긴 힘들 것 같다.데이터 베이스 초기화를 하여 데이터를 삽입을 하는 작업을 한다.코틀린을 써본 적이 없어서 아직 너무 미숙하다. 그리고 뭐가 뭔진 잘 모르겠지만지금 데이터를 넣을 때mutableListof 라는 메서드를 이용하여 삽입하고 있다.그리고 데이터를 삽입할 때컬럼이 여러개일 경우그 컬럼의 값이 안 들어가게 되면 오류가 난다.값이 여러개 일 경우 mutableListof 를 사용하여 리스트화 해서saveall 이라는 키워드를 사용해 값을 집어 넣는다.saveall은 영속성으로 값을 집어 넣는 것이다.실습을 하던 도중 Error creating bean with name '*': Invocation of init method failed이 에러가 떠 인터넷에 찾아보다가 강의를 다시 들으며 오타를 낸 부분이 있나 찾아봤더니skill부분에 project로 의존주입을 해서 에러가 난 거였다.테스트 코드는 매우 중요하다.인텔리제이는 테스트코드를 작성하기 매우 용이하다테스트 패키지안에코틀린 파일을 만든다그리고 어노테이션을 @DataJpaTest 이것을 부착해준다.@DataJpaTest은 필요한 기능들만 초기화해준다롤백을 안해주면 다음 테스트에 영향을 미칠 수 있다트랜잭션 어노테이션을 달아준다@TestInstance(TestInstance.Lifecycle.PER_CLASS)한번 만들어주고 클래스 단위로 돌아가게 해준다.Assertions.assertThat(beforeInitialize).hasSize(0)테스트 통과 실패여부를 알려주는 코드테스트 코드 실행 후 실패 에러defined in @EnableJpaRepositories declared on JpaRepositoriesRegistrar.EnableJpaRepositoriesConfiguration: Could not create query for public abstract long확인해보니 httpinterfaceRepostory의 메서드의 이름에 오타가 나있었던 것. 

워밍업클럽3기_미션3_지표 설계

미션프로덕트에서 어떤 지표를 측정할지 설정하기. 지표의 이름을 정하고 지표의 계산식을 정의해보기. 해당 지표를 측정해야 하는 이유는 무엇인지, 지표의 변동에 따라 어떤 후속 조치를 해야 하는지 작성하기 지표 설계(1) 지표 설정 목적과 방향새로 추가한 기능을 사용자들이 원활하게 사용하고 있는지 추적이 필요한 상황이라, 신규 사용자의 기능 활성화 지표를 측정 지표로 설정하였다.새로 추가한 기능의 사용 플로우는 아래와 같다.*플로우: 메인 화면 -> 코치마크 -> 미리 만들어 둔 프로필 선택/프로필 생성 -> 시간 입력 -> 휴대폰 화면 제한 시작 -> 종료/ 이탈서비스의 핵심 가치는 휴대폰 사용을 제한하는 신규 기능을 사용자의 라이프 스타일에 맞게 커스텀하여 활용하는 것이다. 따라서 활성화 지표를 Set up, Aha, Habit 단계로 구분하여 보았다.Set up: 미리 만들어 둔 프로필을 선택하여 휴대폰 화면 제한을 시작한 후에 종료 또는 이탈하는 것을 목표로 둠. 제한 종료 또는 이탈을 이벤트를 발생시킨 신규 사용자 수 측정*수집 가능한 이벤트: 메인 화면 -> 프로필 선택 -> 시간 입력 -> 제한 시작 버튼 클릭 -> 제한 화면 -> 제한 종료 또는 이탈Aha: 사용자가 새로 프로필을 생성하여 저장하는 것을 목표로 둠. 저장 버튼 클릭 이벤트를 발생 시킨 신규 사용자 수 측정*수집 가능한 이벤트: 메인 화면 -> 프로필 추가 버튼 클릭 -> 프로필 정보 입력 화면 -> 프로필 정보 입력 -> 저장 버튼 클릭Habit: 사용자가 일주일에 3회 이상 화면 제한 기능을 사용하는 것을 목표로 둠. 제한 또는 이탈 이벤트를 가입 후 일주일 이내에 3회 이상 발생시킨 신규 사용자 수 측정*수집 가능한 이벤트: 메인 화면 -> 프로필 선택 -> 시간 입력 -> 제한 시작 버튼 클릭 -> 제한 화면 -> 제한 종료 또는 이탈(2) 지표 이름과 계산식신규 사용자: first_open 이벤트와 로그인 이벤트, 그리고 데이터 새로 가져오기 이벤트가 모두 일어난 사용자. 데이터 새로 가져오기 이벤트를 기준으로 7일 간 신규 사용자로 처리Set up = 신규 사용자이면서 제한 종료 또는 이탈 이벤트를 발생 시킨 사용자 수/ 메인 화면을 방문한 신규 사용자 수*100Aha=신규 사용자이면서 프로필 저장 버튼을 클릭한 사용자 수/ 메인 화면을 방문한 신규 사용자 수*100Habit=신규 사용자이면서 제한 종료 또는 이탈 이벤트를 3회 이상 발생 시킨 사용자 수/ 메인 화면을 방문한 신규 사용자 수*100. 7일간 기간을 설정한 후 계산할 것(3) 후속 조치각 단계의 %를 살펴보며 기능의 사용자 플로우에서 개선이 필요한 부분을 발굴하여 개선 방향을 고민한다.

기획 · PM· PO

gptjddl777

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

미션 3. 프로덕트 지표 설정하기여러분이 맡은 프로덕트에서 어떤 지표들을 측정할지 설정해보세요.단, 프로덕트 지표 프레임워크 강의에서 소개한 지표들은 제외합니다. 지표의 이름을 정하고, 지표의 계산식을 정의해보세요.그 지표를 측정해야 하는 이유는 무엇인지, 지표의 변동에 따라 어떤 의사결정 또는 후속조치를 해야 하는지 작성해보세요.프로덕트 : 올리브영지표명 : 배송유형별 선택율계산식 : 각 배송유형(일반배송/오늘드림/픽업) 장바구니 수 / 전체 장바구니 수 X 100측정이유 : 사용자들이 선호하는 배송유형을 파악하고, 강화 혹은 개선이 필요한 배송유형을 확인하기 위함 의사결정/후속조치 : 선택율이 가장 높은 배송유형의 배송방법을 더 강화하고, 선택율이 가장 낮은 배송유형을 개선하기 위한 프로모션 및 마케팅 강화 2. 지표명 : 배송유형별 구매전환율계산식 : 각 배송유형별 주문완료 건수/ 각 배송유형을 선택한 건수 X 100측정이유 : 각 배송유형에 따른 구매전환율을 확인하여, 강화 혹은 개선이 필요한 배송유형을 파악하기 위함의사결정/후속조치 : 구매전환율이 낮은 배송유형에 대한 구매혜택 추가제공 및 배송유형 정보 추가안내를 통해 장바구니 내 구매전환율 개선3. 지표명 : 배송유형별 장바구니 이탈률계산식 : 각 배송유형별 → 결제하지 않고 이탈한 장바구니 수 / 장바구니 수 X 100측정이유 : 각 배송유형에 따른 이탈률을 확인하여, 이탈 요인을 파악하기 위함의사결정/후속조치 : 특정 배송유형에 대한 이탈률이 높은경우 해당 배송유형의 불편한점, 이탈하게 되는 요인을 찾아 이탈을 최소화 하기 위한 배송방법 개선점 도출 

기획 · PM· POPMPO

Jaeeun Jeong

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

미션 3. 프로덕트 지표 설정하기 여러분이 맡은 프로덕트에서 어떤 지표들을 측정할지 설정해보세요.단, 프로덕트 지표 프레임워크 강의에서 소개한 지표들은 제외합니다.맡고 있는 프로덕트가 없는 경우, 프로덕트를 하나 정해서 해 보세요.지표의 이름을 정하고, 지표의 계산식을 정의해보세요.그 지표를 측정해야 하는 이유는 무엇인지, 지표의 변동에 따라 어떤 의사결정 또는 후속조치를 해야 하는지 작성해보세요.지표 대상 서비스화해의 마케팅 서비스 지표1: 사용자 마케팅 채널지표: 자사 이벤트 시 컨텐츠의 채널별 참여자 수 확인계산식: (각 채널별 이벤트 참여자 수) / (해당 콘텐츠 게시 후 일정 기간 내 참여자 수)지표 이유: 마케팅에 사용시 효과적인 광고 채널을 파악할 수 있고, 채널별 효과성도 파악 가능하다.의사결정: 참여자가 많은 채널을 집중적으로 활용하여 마케팅 비용을 절감할 수 있음. 참여자가 적은 채널은 광고 전략 및 콘텐츠 기획 방향을 수정하거나 우선 순위를 낮출 수 있다.후속 조치참여자 증가 : 해당 채널의 콘텐츠 강화 및 예산 추가하여 지속적인 활용참여자 감소 : 콘텐츠 수정 및 다른 채널로의 이동 지표2. 외부 업체와 협업시 효과적인 채널 확인지표 : 외부 업체(인플루언서, 블로그, SNS 광고, 이벤트 부스)를 이용한 캠페인을 통해 유입된 가입 및 참여자 수 확인계산식: (각 캠페인 별 유입자 수 / 캠페인 활동 기간) / 소요된 비용측정 이유: 단가 대비 캠페인 참여자 수 파악을 통해 효과적인 캠페인 채널을 파악하여 외부 업체와 협업시 방향성을 정할 수 있다.의사결정:효율이 높은 캠페인 : 적극적으로 활용 및 협업 강화효율이 낮은 캠페인 : 캠페인을 중단하거나 재검토를 통해 전략 방향 수정지표 변동에 따른 의사 결정 및 후속 조치효율적인 채널: 더 많은 예산 투입 및 추가 캠페인 기획비효율적인 채널: 해당 채널의 캠페인 종료 및 캠페인 방향/기획 재검토 지표3. 외부 업체와 협업을 통해 유입된 사용자의 리텐션율 확인지표: 외부 업체(인플루언서, 블로그, SNS 광고, 이벤트 부스)를 통해 유입된 사용자의 리텐션율 (재방문율, 지속 사용 비율 등)계산식: (외부 업체를 통해 유입된 사용자가 일정 기간 내 다시 방문하거나 활동한 비율) / (전체 외부 업체 유입 사용자 수)측정이유: 유입된 사용자 중 실제로 지속적으로 활동하는 사용자의 비율을 확인하여, 외부 협업의 효과를 장기적으로 평가할 수 있습니다. 리텐션율을 통해 유입된 사용자가 얼마나 브랜드에 충성도 높은지, 협업의 장기적인 효과를 평가할 수 있습니다.지표 변동에 따른 의사 결정 및 후속 조치: 높은 리텐션율: 해당 업체와의 추가 협업을 고려하거나, 동일한 전략을 다른 외부 업체와 적용.낮은 리텐션율: 타겟층 고려 및 외부 업체의 협업 퀄리티 등의 파악을 통해 원인을 분석하여 업체를 변경하거나 전략을 수정  회고다 작성하고 보니 제품을 성장시키고 발전시키는 부분보다 제품을 광고하고 홍보하는 지표로서 작성된거 같아서 조금 아쉽네요.캠페인에 사용된 기능들의 사용자의 사용 빈도나 사용 시간등의 파악을 통해 제품의 문제점 파악 및 개선의 지표로 사용할 수도 있을 거 같습니다.

Day 16 미션 : 레이어 아키텍쳐의 테스트

이번에 설명하는 레이어는 영속성 레이어, 비즈니스 레이어, 프레젠테이션 레이어를 기준으로 설명하겠습니다.레이어의 특징어떻게 테스트하면 좋을지영속성 레이어영속성 레이어?DB에 접근하는 계층비즈니스 로직이 들어가지 않은 순수하게 데이터에 대한 처리 및 조회를 수행영속성 레이어의 테스트무엇을 확인해야할까?원하는 데이터에 정확히 접근하는지쿼리가 길어졌을 때, 내가 원하는 데이터에 맞게 작성되었는지어떻게 테스트를 해야할까?영속성 계층이 의존하는 계층이 대부분 상황에서 없기 때문에 단위 테스트 형식으로 진행비즈니스 레이어비즈니스 레이어?비즈니스 로직이 전개되는 계층영속성 레이어가 사용된다.도메인 개념이 적용트랜잭션 개념 적용비즈니스 레이어의 테스트하나의 트랜잭션을 보장하는지 확인비즈니스 로직이 정확히 수행되는지 확인여러 케이스에 대해 테스트하자.프레젠테이션 레이어프레젠테이션 레이어란?외부 세계와 가장 가까운 계층요청과 관련한 데이터를 받는다.요청에 대한 데이터를 전달한다.프레젠테이션 레이어의 테스트요청에서 건너온 값들에 대한 검증도메인 규칙을 제외한 간단한 검증상황에 대한 정확한 응답이 반환되는지 확인

백엔드테스트레이어

징니

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

💻 강의입문자를 위한 Spring Boot with Kotlin - 나만의 포트폴리오 사이트 만들기 📚 학습Data ClassData Class가 무엇이고, 어떤 용도로 사용되는지 찾아봤다Data Class 참고CustomExceptionJava Spring Project를 할 때 ErrorCode를 enum으로 관리해 CustomException을 사용한 적이 있다미니 프로젝트에 CustomException을 적용하려고 Kotlin으로 변환해보니 막히는 부분이 있었지만, 참고한 자료 덕분에 해결할 수 있었다CustomException 참고 { "timestamp": "2025-03-18T01:46:23.5967528", "status": 404, "message": "사용자를 찾을 수 없습니다." }아쉬운 점이번 주는 몸이 안 좋아서 평일 동안 회복하는 데 집중했고, 주말에는 평일에 못한 미션 4와 미션 5를 제출하였다작년 9월부터 지금까지 부트캠프와 스터디를 하면서 실력 향상에 집중하다 보니 피로가 쌓여 면역력이 떨어졌다😥건강 관리를 못해 결국 이번 주에 강의를 많이 듣지 못한 부분이 아쉽다회고토요일 오전에 수액을 맞고 많이 괜찮아져서 다음 주는 이번 주에 못한 만큼 열심히 해야겠다다음 주는 강의도 듣고, 미니 프로젝트 기능 보완, 인증/인가 구현, 예외 처리를 해야겠다  🎯 미션 4와 미션 5조회 REST API 만들기조회 API를 개발한 뒤 테스트 코드를 작성테스트 케이스는 3개 이상, 모든 케이스가 어떤 환경에서도 성공해야 함커밋 메시지 : [미션4] 조회 REST API 만들기미션4 제출 스레드에 깃허브 커밋 링크를 공유삽입, 수정, 삭제 REST API 만들기삽입, 수정, 삭제 API를 개발한 뒤 테스트 코드를 작성테스트 케이스는 API별로 3개 이상, 모든 케이스가 어떤 환경에서도 성공해야 함커밋 메시지 : [미션5] 삽입, 수정, 삭제 REST API 만들기미션5 제출 스레드에 깃허브 커밋 링크를 공유문제테스트 케이스를 3개 이상 작성해야 하는데 어떤 경우로 나뉘어서 작성해야 하는지 잘 모르겠다아직 테스트 케이스를 작성하는 것은 어려워서 일단 Repository에서 사용하는 메서드가 제대로 동작하는지 테스트 코드를 작성해 확인하였다findAll()findById()findDepartmentByCode()findCourseByIdAndStudent()회고findCourseByIdAndStudent() 메서드가 잘 동작하는지 테스트 코드를 작성했을 때 의도한 대로 결과가 나와서 뿌듯했다아직은 간단하게 테스트 코드를 작성할 수 있을 정도이지만, 계속 하다 보면 익숙해질 것 같다... @Test fun testFindCourseByIdAndStudent() { logger.info { "findCourseByIdAndStudent 테스트 시작" } val student = userRepository.findById(1L).get() val course = courseRepository.findCourseByIdAndStudent(1L, student).get() logger.info { "학생 이름: ${student.name}" } logger.info { "수강 과목의 학생 이름: ${course.student.name}" } assertThat(course.student).isEqualTo(student) logger.info { "findCourseByIdAndStudent 테스트 종료" } } }2025-03-23T21:26:12.090+09:00 INFO 5532 --- [ main] c.k.a.d.c.r.CourseRepositoryTest : findCourseByIdAndStudent 테스트 시작 Hibernate: select u1_0.id, u1_0.academic_year, u1_0.code, u1_0.created_at, u1_0.department_id, u1_0.login_id, u1_0.name, u1_0.password, u1_0.role, u1_0.updated_at from users u1_0 where u1_0.id=? Hibernate: select c1_0.id, c1_0.created_at, c1_0.student_id, c1_0.subject_id from course_enrollment c1_0 where c1_0.id=? and c1_0.student_id=? 2025-03-23T21:26:12.163+09:00 INFO 5532 --- [ main] c.k.a.d.c.r.CourseRepositoryTest : 학생 이름: 학생 2025-03-23T21:26:12.164+09:00 INFO 5532 --- [ main] c.k.a.d.c.r.CourseRepositoryTest : 수강 과목의 학생 이름: 학생 2025-03-23T21:26:12.165+09:00 INFO 5532 --- [ main] c.k.a.d.c.r.CourseRepositoryTest : findCourseByIdAndStudent 테스트 종료

백엔드

바커스

콘텐츠마케팅 트래픽 설계 | 온라인 비즈니스 성공을 위한 핵심 전략4가지

🔍 트래픽 설계의 의미와 핵심 개념단순히 방문자 수를 늘리는 것이 아니라, 타겟 고객이 있는 채널을 공략하고 이들을 비즈니스 자산으로 전환하는 전략입니다. 광고만으로는 매출로 이어지지 않으며, 체계적인 트래픽 설계가 필수입니다.💡 왜 트래픽 설계가 중요한가?좋은 제품도 노출되지 않으면 의미 없습니다. 브랜드 인지도 확보, 리드 수집, 매출 증대, 장기적인 유입 유지 등 비즈니스 성장을 위한 토대가 되며, 소유 트래픽 구축이 핵심입니다.📈 단기 vs 장기 트래픽 전략의 차이단기 트래픽은 빠른 유입이 가능하지만 지속성이 낮고 광고비가 필요합니다. 반면, 장기 트래픽은 시간은 걸리지만 안정적인 유입이 가능해 장기적으로 더 효율적인 구조를 만들 수 있습니다.✅ 실전 사례로 보는 트래픽 설계사례 1: 유튜브와 이메일 리스트를 통해 콘텐츠 기반 장기 트래픽 구축, 수천 명의 구독자와 매출 증가 달성.사례 2: 인스타그램 콘텐츠 바이럴로 SNS 유입 확대, 웹사이트 및 구독자로 유도해 방문자 5배 증가.🛠 실행 가능한 전략 4가지① 타겟 고객이 있는 커뮤니티에서 콘텐츠로 자연스럽게 노출② 이메일, 유튜브 등 소유 트래픽 자산 구축③ 단기 광고와 장기 콘텐츠 전략 병행④ 분석 툴을 활용해 최적화 지속적으로 진행좀 더 상세한 정보가 필요하시면 아래 링크를 참조하시기 바랍니다.콘텐츠마케팅 트래픽 설계란 무엇인가?(온라인 비즈니스)

마케팅콘텐츠마케팅콘텐츠마케팅트래픽콘텐츠마케팅트래픽설계온라인비즈니스

junghlee234

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

학습 내용저번 주에 처리하지 못한 입력 및 디스플레이 컴포넌트 관련 내용을 학습하였습니다.  이번 주와 관련하여 피드백 컴포넌트 관련 내용을 학습하였습니다.이번 주 발자국을 작성하고 난 후 네비게이션 컴포넌트와 모드 관련 내용을 학습할 예정입니다.미션 및 실습특히, 이번 주 컴포넌트 실습에 있어서 '티끌 모아 태산'이라는 말이 많이 생각이 났습니다. 여러 컴포넌트를 불러와 인스턴스로 또 하나의 컴포넌트를 조합하는 일이 매우 많았기 때문에, 하나의 컴포넌트가 잘 완성이 되어 있지 않으면 연쇄적으로 영향을 주는 경우가 많았습니다.프로토타입을 피그마에서 사용시 스피너, 스켈레톤을 움직이게 만드는데 활용한 경우는 없었고, 터치로 인한 스크린 전환 같은 UI/UX 흐름에만 활용을 했었는데 관련하여 많은 배움을 얻을 수 있었습니다.사실 개발시에도 라이브러리를 import 하는 식으로 구현하는 경우가 많아서 이번 구현이 처음이었습니다.회고이번 주는 아직 전체 진도를 따라가진 못했지만, 정말 많은 것들을 배울 수 있었던 주였습니다.오늘 밤과 내일 사이에 남은 진도를 따라 잡아 다음 주부터는 여유롭게 지금까지 했었던 것들을 차근차근 확인하며 마무리 할 수 있는 시간을 가지도록 노력할 것입니다.

UX/UI인프런_워밍업_클럽UX/UIFigma디자인시스템볼드UX

hee j

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

목차다른 페이지에서 데이터를 받아 전달rocoil을 사용하는 방법Supabase에서 maybeSingle()과 single()react-intersection-observeruseInView 함수 사용법useInfiniteQuery 기본 사용법3주차 미션supabase에 컬럼 추가찜 기능 설정하기찜한 영화를 화면 최상단으로 보여주도록 정렬Netflix Clone Project 1. 다른 페이지에서 데이터를 받아 전달search 검색 란은 header에 있고 해당 값을 전달해서 받아 오는 곳은 movie-card-list.tsx 에 있으므로 Recoil(전역 상태 관리 라이브러리)을 사용하여 해당 값을 넘겨준다reccoil도 레이아웃에서 react query와 materia ui를 사용할 수 있게 해준 것처럼 <RecoilRoot>가 있음하지만 recoil은 기본적으로 클라이언트 라이브러리라 별도의 provider를 정의 해준다.(app/config/RecoilProvider.tsx)[rocoil을 사용하는 방법]atom 함수를 사용/utils/recoil/atoms.ts 파일 생성recoil을 사용할 페이지에 atomes.ts에 생성한 search atom을 넣어줌ex) header.tsx const {search, setSerch} = useRecoilState(searchState)ex) movie-card-list.tsxqueryKey에 search 값을 넣어야 search 값이 변경될 때마다 query function이 재호출 됨 const search = useRecoilValue(searchState); const getAllMoviesQuery = useQuery({ queryKey: ["movie",search], queryFn: () => searchMovies({ search }), }); [Supabase에서 maybeSingle()과 single()]single(): 반환되는 데이터가 무조건 한 행이여야 하며, null이 존재 또는 데이터 1개 초과 조회 시 오류 발생maybeSingle(): 반환되는 데이터에 null이 존재해도 오류가 발생하지 않고, 빈 값을 반환함 2. react-intersection-observerreact-intersection-observer 설치npm install react-intersection-observer화면에 이 컴포넌트가 몇 퍼센트 들어왔을 때, inView 값이 true가 됨즉, 특정 요소가 화면에 노출되었는지 감지하는 기능import { useInView } from 'react-intersection-observer'; const [ref, inView, entry] = useInView({ threshold: 0, });ref는 감지할 요소에 연결해야 하는 참조이며, inview는 해당 요소가 화면에 노출되었는지 여부를 나타내는 불리언 값즉, 현재 observe할 엔티티에 대해 레퍼런스를 넣기 위한 값<div ref={ref}> {inView && 'Element is in view!'} </div> ** 우리가 왜 이것을 사용해야 하나?스크롤 맨 아랫부분에 보이지 않는 태그를 넣어서 해당 태그가 보이면 다음 페이지를 가져올 수 있도록 함수를 만들 예정즉, 페이징을 커서 방식으로 개발[useInfiniteQuery]기존에 사용한 useQuery로 무한 스크롤을 구현하기에는 매우 복잡함(다양한 값 필요)useInfiniteQuery는 react-query 라이브러리의 핵심 기능 중 하나입니다. 이를 사용하면 무한 스크롤과 같은 기능을 쉽게 구현할 수 있다.isFetchingNextPage: isLoading 대신 사용fetchNextPage: 다음 페이지hasNextPage: 가지고 있는 다음 페이지const { data, fetchNextPage, hasNextPage, isFetchingNextPage, status, } = useInfiniteQuery('todos', fetchTodos, { getNextPageParam: (lastPage, pages) => lastPage.nextPage, });3. 3주차 미션 [구현 이미지][supabase에 컬럼 추가]찜 기능을 구현할 favorit 이라는 컬럼 추가0이면 false, 1이면 true[찜 기능 설정하기]하트를 클릭하면 하트의 상태가 바뀌면서 데이터 저장import { updateFavorit } from "actions/movieActions"; import Link from "next/link"; import { useState } from "react"; export default function MovieCard({ movie }) { const [isFavorit, setIsFavorit] = useState(movie.favorit); const handleClick = async () => { setIsFavorit(!isFavorit); await updateFavorit(movie.id, movie.favorit); }; return ( <div className="col-span-1 relative"> {isFavorit ? ( <button onClick={handleClick} className={`absolute top-2 right-2 z-20 fa-solid fa-heart text-red-500 text-3xl`} /> ) : ( <button onClick={handleClick} className={`absolute top-2 right-2 z-20 fa-regular fa-heart text-red-500 text-3xl`} /> )} {/* image */} <div> <img src={movie.image_url} className="w-full" /> <Link href={`/movies/${movie.id}`}> <div className="absolute flex items-center justify-center top-0 bottom-0 left-0 right-0 z-10 bg-black opacity-0 hover:opacity-80 transition-opacity duration-300"> <p className="text-xl font-bold text-white">{movie.title}</p> </div> </Link> </div> </div> ); }아이디와 상태 값을 가져와 1 이면 0, 0이면 1로 바꾸어 update해줌 export async function updateFavorit(id, state) { const supabase = await createServerSupabaseClient(); state = state == 1 ? 0 : 1; const { data, error } = await supabase .from("movie") .update({ favorit: state, }) .eq("id", id); handleError(error); return data; }   [찜한 영화를 화면 최상단으로 보여주도록 정렬]favorit 값을 0과 1로 설정했기 때문에 order에서 ascending를 사용해 내림차순으로 정렬export async function searchMovies({ search, page, pageSize }) { const supabase = await createServerSupabaseClient(); const { data, count, error } = await supabase .from("movie") .select("*", { count: "exact" }) .like("title", `%${search}%`) .order("favorit", { ascending: false }) .range((page - 1) * pageSize, page * pageSize - 1); const hasNextPage = count > page * pageSize; favorit 값이 1이면 꽉찬 하트, 0이면 빈 하트로 보여주며, 이미지보다 상단에 띄워 놓아 클릭 시 해당 값이 바뀌도록 설정함export default function MovieCard({ movie }) { return ( <div className="col-span-1 relative"> <button className={`absolute top-2 right-2 z-20 ${ movie.favorit ? "fa-solid fa-heart" : "fa-regular fa-heart" } text-red-500 text-3xl`} /> <div> <img src={movie.image_url} className="w-full" /> <Link href={...}> <div className="absolute flex items-center justify-center top-0 bottom-0 left-0 right-0 z-10 bg-black opacity-0 hover:opacity-80 transition-opacity duration-300"> <p className="text-xl font-bold text-white">{movie.title}</p> </div> </Link> </div> </div> ); } 

풀스택풀스택워밍업

huihui

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

학습 내용섹션6. Spring & JPA 기반 테스트Layered Architecture: 관심사의 분리 때문에 레이어를 분리해야 함Persisitence Layer쿼리가 의도대로 작성되었는지 확인쿼리를 구현하는 기술이 바뀌어도 기능의 동작을 보장하도록 테스트를 작성@DataJpaTestBusiness Layer비즈니스 로직 흐름과 트랜잭션 처리 검증트랜잭션을 보장하는지 확인@TransactionalPresentation Layer외부 요청, 파라미터 위주로 검증의존관계를 가짜 객체를 사용해(Mocking) 환경 재현@WebMvcTest, @MockBean, @MockMvc테스트 코드단위 테스트: 작은 코드 단위를 독립적으로 검증하는 테스트통합 테스트: 여러 모듈이 협력하는 기능을 통합적으로 검증하는 테스트  회고이번주는 Layered Architecture 구조의 Layer별로 테스트를 작성해보며 TDD를 적용하는 과정을 배웠다.점점 수업 내용을 이해하고 내 것으로 만들기 어려워지는 것 같다. 워밍업 클럽 시작 전 커리큘럼을 보고 학습양이나 난이도가 만만치 않겠다고 생각하긴 했지만, 직접 수업을 들어보니 내 부족한 점들이 더 잘 보인다. 이번주가 특히나 힘들었는데, 진도를 맞추는 데 급급해 강사님이 라이브코딩하는 것을 보며 코드를 따라치는게 고작이었다. 그래서인지 수업을 다 들어놓고도 내가 제대로 배우고 이해한 게 맞나?라는 의문이 머릿속을 떠나지 않았던 한 주였다. 우선은 완주를 목표로 진도표에 맞춰 학습하고, 일정이 끝난 후 강의를 다시 들으며 개념을 보강하는 과정이 필요할 것 같다.

백엔드

수뼈

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

[Day 11~13]Algorithm정렬 알고리즘(Sorting Algorithm)개요데이터셋이 주어졌을 때, 이를 사용자가 지정한 기준에 맞게 정렬하여 출력하는 알고리즘.참고: 정렬 알고리즘은 왜 배워야 할까?대표적인 정렬 알고리즘버블 정렬(Bubble Sort) (Day 09 참고)선택 정렬(Selection Sort) (Day 09 참고)삽입 정렬(Insertion Sort)개요 및 특징자료 배열의 모든 요소를 앞에서부터 차례대로 이미 정렬된 배열 부분과 비교해, 자신의 위치를 찾아 삽입하는 알고리즘(참고).구현이 쉬운 편에 속하나 성능은 O(n^2)로 매우 낮음.구현하기첫 번째 구현: 강의 보며 구현 (Done)두 번째 구현: 강의 안 보고 구현하다 기억 안 나면 강의 확인하며 구현 (Done)세 번째 구현: 강의 안 보고 구현하다 기억 안 나면 과거에 내가 짠 코드 확인하며 구현 (Done)네 번째 구현: 아무것도 안 보고 구현 (Done)마지막 구현: 익숙한 파이썬으로 구현(+리팩토링(비파괴적 연산, 예외처리)) (Done)병합 정렬(Merge Sort)개요 및 특징Array를 정렬의 최소 단위(=1개)로 쪼개 정렬한 결과를 병합하여 정렬하는 알고리즘.Devide and Conquer 기법과 재귀 함수를 이용해 정렬함(참고).참고: 여기서 설명하는 건 mergeSort()가 아니라 merge()다. 저걸 누가 헷갈리냐고? NADA재귀적으로 구현해야 하므로 이해와 구현이 어려우나, 성능(O(nlogn))이 매우 높음.구현하기첫 번째 구현: 강의 보며 구현 (Done)두 번째 구현: 강의 안 보고 구현하다 기억 안 나면 강의 확인하며 구현 (Done)세 번째 구현: 강의 안 보고 구현하다 기억 안 나면 과거에 내가 짠 코드 확인하며 구현 네 번째 구현: 아무것도 안 보고 구현 마지막 구현: 익숙한 파이썬으로 구현(+ 리팩토링) 퀵 정렬(Quick Sort)개요 및 특징피벗(Pivot)을 기준으로 작은 값과 큰 값을 분할하며 정렬하는 알고리즘.Devide and Conquer 기법과 재귀 함수를 이용해 정렬함(참고).pivot에 따라 Θ(nlogn), O(n^2)로, 안정적으로 O(nlogn)의 성능을 뽑아내는 병합 정렬이 더 좋음.하지만, 같은 O(nlogn)이라면 Locality of Reference 원리 때문에 더 빠름.구현하기첫 번째 구현: 강의 보며 구현 (Done)두 번째 구현: 강의 안 보고 구현하다 기억 안 나면 강의 확인하며 구현 세 번째 구현: 강의 안 보고 구현하다 기억 안 나면 과거에 내가 짠 코드 확인하며 구현 네 번째 구현: 아무것도 안 보고 구현 마지막 구현: 익숙한 파이썬으로 구현(+ 리팩토링) Operating System가상 메모리(Virtual Memory)개요실행하고자 하는 프로그램을 일부만 메모리에 적재하여 실제 물리 메모리 크기보다 더 큰 프로세스를 실행할 수 있게 하는 기술(혼공컴운 402p.)가상 메모리 시스템에서 MMU는 동적 주소 변환(Dynamic Address Translation; DAT)을 통해 RAM의 물리 주소와 스왑 영역을 하나의 가상 주소로 보고 관리함(참고).가상 주소-물리 주소쌍은 매핑 테이블로 관리됨(참고).프로세스 입장에서 관리 편의성 향상을 위해 생성된 가상의 공간이므로 이론상 크기는 ∞이자만, 실제로는 CPU 비트 수에 따라 OS에 의해 적정 크기로 결정됨(참고).하지만 사용자의 성향에 따라 수동 조정도 가능함(참고).OS가 실행할 프로세스 외 프로세스를 스와핑(Swapping)하는 방식으로 구현되어 있음.주소 공간 분할 방식세그멘테이션(Segmentation) 개요 및 특징프로세스를 세그멘트(Segment)로 잘라서 메모리에 배치하는 방법.MMU는 Segment Table을 통해 DAT하여 각 프로세스의 논리 주소를 물리 주소로 변환함(참고: 1, 2).Segmentation Table이 아님!메모리의 가변적 분할+프로세스 내 각 영역의 메모리 접근 보호 용이성 vs. 외부 단편화DAT 과정 (참고: 1, 2, 3)프로세스가 코드를 실행해 필요한 명령어와 데이터의 가상 주소를 CPU에 전달함.CPU가 MMU에게 특정 논리 주소의 물리 주소 정보를 요청함.MMU가 Segment Table의 Base Address와 Bound Address를 통해 해당 가상 주소와 연결된 물리 주소를 찾음.MMU가 가진 STBR을 통해 S.T.의 물리 메모리상 시작 위치를 찾음.S.T.상 해당 프로세스의 가상 주소가 몇 번째 세그먼트에 위치해 있는지 찾음.세그먼트 테이블의 해당 인덱스로부터 찾은 Base Address와 Bound Address 값을 참고해 진행.가상 주소 값 < Bound Address 값이라면 Base Address 값+가상 주소 값으로 물리 주소 값을 알아냄.가상 주소 값 > Bound Address 값이라면 프로세스를 종료시킴.페이징(Paging) (참고: 1, 2, 3, 4)개요 및 특징프로세스의 논리 주소 공간을 페이지(Page) 단위로, 물리 주소 공간을 프레임(Frame)으로 자른 뒤 페이지를 프레임에 할당하는 가상 메모리 관리 기법(혼공컴운 403p.)페이지 테이블의 크기가 너무 커져서 메모리를 낭비하지 않도록 주의하는 것이 관건.메모리의 효율적 관리 vs. 내부 단편화DAT 과정 (참고: 1, 2, 3)프로세스 코드 실행 ... CPU의 특정 논리 주소의 물리 주소 정보 요청까지는 동일함.MMU가 Page Table을 통해 해당 가상 주소와 연결된 물리 주소를 찾음.해당 가상 주소의 Page Number 및 Offset을 통해 고유 Page Table에 접근함.Page Number = 가상 주소 / 페이지 크기, Offset = 가상 주소 % 페이지 크기PTBR을 통해 P.T.의 물리 메모리상 시작 위치를 찾음.S.T.상 페이지 번호로 프레임 번호를 찾고, 해당 프레임의 시작 주소에 Offset을 더해 변환함.페이지드 세그멘테이션(Paged Segmentation)개요 및 특징프로그램을 Segment로 나누고, 이를 Page들의 집합으로 구성하는 방식(참고).DAT 시 외부의 세그먼트 테이블과 내부의 페이지 테이블로 이루어진 두 단계 테이블을 이용함(참고).세그먼트 테이블 변경 사항보호 비트(Protection Bit) 추가(참고).Base Address -> Page NumberBound Address -> 해당 세그먼트의 페이지 개수Paging과 Segmentation의 장점 vs. 페이지 이중 참조로 인한 Overhead따라서, 현대 OS에서는 페이징과 페이지드 세그멘테이션을 혼용하여 사용함.DAT 과정 (참고: 1, 2, 3)프로세스 코드 실행 ... CPU의 특정 논리 주소의 물리 주소 정보 요청까지는 동일함.S.T.상 해당 프로세스의 가상 주소가 몇 번째 세그먼트에 위치해 있는지 찾음.영상에서 "0x12300 번지이니 1번 세그먼트구만!"이라는 설명은 예시일 뿐, 번지 수와 세그먼트 번호 간의 관계는 없음(참고).해당 세그먼트의 접근 권한 위반 여부 확인위반이라면 해당 요청을 한 프로세스를 Shutdown시킴.페이지 넘버를 통해 프레임 넘버를 가져 온 후, 해당 프레임으로 접근함.Invalid라면 Swap 영역에서 가져옴.해당 프레임의 시작 주소로부터 offset을 더해 원하는 데이터에 접근함(참고: 1, 2).  [Day 14]Algorithm메모이제이션(Memoization) (참고: 1, 2, 3)정의 및 특징함수 호출의 결과를 캐싱하고 동일한 입력이 다시 발생할 때 캐싱된 결과를 반환하는 하향식 접근(참고).공간 복잡도-시간 효율성 간 Trade-off 관계에 있음.계산 결과를 저장하기 위한 추가 메모리가 필요하지만, 일반적으로 중복 계산 제거로 얻는 시간 효율성(Time Effeciency)이 훨씬 높으므로 자주 활용됨.메모이제이션 예제: 피보나치 수열(참고).메모이제이션 없이 구현메모이제이션 활용해 구현Operating System입출력 장치(I/O Device)개요새로운 데이터를 받아들여서 중앙 처리기로 보내고 다시 처리 결과를 받아 알아볼 수 있는 형태로 바꾸어 주는 장치(참고).CPU와 메모리 버스에 직접 연결되지 않고, 간접적으로 연결되면 전부 I/O Device!(참고)유저가 시스템과 소통할 수 있게 하는 장치로, 다른 장치에 비해 처리 속도(=전송률)가 매우 느림.이를 보완하는 기술로 Buffering과 Spooling이 존재함(참고: 1, 2, 3).각 장치는 각자의 Device Controller를 통해 데이터를 보내고, I/O Bus를 통해 CPU를 거쳐 RAM에 도달함.DMA(Direct Memory Access)를 추가해 CPU 없이 I/O Controller가 Event 등 데이터를 RAM에 직접 전달하는 입출력 방식, 즉 Memory Mapped I/O로 확장됨(참고).각 Device의 공통 내부 구조 (※ I/O Device는 종류별, 브랜드별, 제품별로 내부 구조가 천차만별임)Device Controller (참고: 1, 2, 3)System Bus와 장치 사이에서 데이터를 송수신할 수 있도록 Interface 역할을 하는 장치.'해당 I/O 장치를 전담으로 처리하는 작은 전용 CPU 같은 존재'라 이해하면 좋음!(참고)CPU와 I/O Device 간 통신 중개, 오류 검출, 데이터 버퍼링 등의 역할을 수행함.장치 종류에 따라 장치 내부에 있을 수도, 외부(본체)에 있을 수도, 둘 모두에 분산되어 있을 수도 있음.장치의 위치보다 장치의 기능을 위주로 기억하는 것이 핵심!Buffer입출력 장치와 응용 프로그램 사이의 데이터를 임시로 저장하는 메모리 공간.일반적으로 이중 버퍼링, 즉 입력 버퍼와 출력 버퍼를 사용하는 방식이 일반적임.RegistersCPU가 장치 상태를 확인하거나 명령을 보낼 때 사용하는 메모리 공간.데이터 레지스터, 상태 레지스터, 제어 레지스터로 이루어져 있음(참고).데이터 전송 단위에 따른 분류 (※ 한 장치에서도 기능별로 달리 작동하는 기기도 있음) (참고: 1, 2, 3)캐릭터 디바이스(Character Device)정의 및 특징Fixed Size Block로 정보를 기록하는 장치로, Event의 순서가 상관없는 장치에 사용함.B.D.에 비해 데이터 전송 단위가 작음종류마우스(Mouse)정의 및 특징2차원 평면에서의 움직임을 컴퓨터에 전송하는 포인팅 디바이스의 일종.일반적으로 2차원 평면상에서 위치 정보(x, y)와 버튼 클릭 정보, 스크롤 동작 등을 전달함.현대 GUI 기반 OS에서 가장 대중적인 입력장치로 쓰이고 있음.Event 처리 및 전달 과정 (※ 커서 이동 기준)Ball Mouse사용자로부터 Event 발생Optical Mouse 키보드(Keyboard)블록 디바이스(Block Device)개요 및 특징C.D.에 비해 데이터 전송 단위가 큼종류HDD(Hard Disk Drive)정의 및 특징비휘발성, 순차접근이 가능한 컴퓨터의 보조 기억 장치(참고).여러 개의 플래터(Platter)가 회전하며 데이터를 저장하고 읽는 자기식 저장장치임.Flash MemoryCPU와 I/O Device 간 통신 과정MMID (※ 강의에 소개된 방식) PMID  [Day 15]Algorithm타뷸레이션(Tabulation) (참고: 1, 2, 3)정의 및 특징문제를 하위문제로 나눠 계산한 결과를 테이블에 저장한 후, 해당 테이블을 연산에 활용하는 상향식 접근(참고).메모이제이션은 계산 및 저장, 호출을 그때그때 하고, 타뷸레이션은 계산 및 저장을 먼저 해두고 나중에 한꺼번에 호출한다는 게 가장 큰 차이!재귀 호출을 하지 않으므로 공간 복잡도도 낮고, Call Stack 생성/제거 Overhead가 없어 T.E도 높음.반복문의 Overhead는 Loop Unrolling 등의 추가 최적화 여지가 있어 시간 효율성 측면에서는 반복문이 효율적임!단, 일부 상황(예: TSP)에서는 메모이제이션의 시간 효율성이 더 높을 수 있음.재귀적 풀이 과정이 더 가독성이 좋을 때, 계산 가능한 경우의 수에 비해 실제 연산에 쓰이는 연산값은 매우 적을 때 등구현하기타뷸레이션 예제: 피보나치 수열 Operating SystemFile System개요 및 특징 컴퓨터에서 파일이나 자료를 쉽게 발견 및 접근할 수 있도록 보관 또는 조직하는 체제(참고).파일 관리자가 Block 단위로 저장된 Block Device에 Byte 단위로 접근할 수 있도록 변환함. 파일 시스템의 기능파일 및 폴더 생성, 수정, 삭제파일 권한 관리무결성 보장백업 및 복구암호화(Encryption)File Descriptor개요 및 특징Process가 특정 파일에 접근할 때 사용하는 추상적인 값(참고).  [Week Review]일주일 동안 공부하면서 배우고 느낀 점독학으로 CS 공부를 하는 나름의 습관에 감이 잡힌 것 같다.마지막 주 시작을 지난주에 미처 다 이해하지 못한 메모리 분할 방식에 대해 공부하면서 시작했다. 처음엔 정말 이해가 가지 않았지만, 강의를 계속 반복 학습하고, 관련 자료를 찾아보고, 이해가 안 되면 ChatGPT와 충분한 질답을 주고받으면서 공부했다.위 일련의 과정에서 파편화된 지식을 계층적으로 재구성해 정리하는 방법, Hallucination이라는 태생적 한계를 지닌 LLM을 공부에 활용할 때 어느 정도로 참고해야 하는지, 신용할 만한 정보는 어디에 많이 있는지 등에 관한 감이 잡혔다.어려웠던 점강약 조절이 너무 어렵다...감자님 강의를 보면 해당 주제에 대한 감은 오지만 지식이 체계적으로 쌓이는 느낌은 들지 않는다. 이건 감자님 강의가 입문자들을 위한 쉬운 설명에 주안점을 두고 있어서 그런 거지 단점이 아니다. 오히려 나 같은 비전공자들로 하여금 '뭘 공부해야 하지?'에 대한 가이드를 준다는 점에서 최고다.근데 내 성격과 몸이 문제다. 지식을 대충 말고 제대로 정리하다 보니 끝이 없다. 이러다간 이번 주는 발자국을 제때 완성 못하고 스터디도 준비 못할 것 같다.어찌저찌 마무리했다... 휴... 원래 내 방식은 노트 정리를 탑다운으로 틀만 잡고 그 후부터는 바텀업으로 디테일을 계속 파고드는 스타일이었는데, 탑다운으로 강의 내용을 어느 정도(한 60% 정도?) 머릿속에 집어넣기 전까진 정리라는 행위를 하지 않기로 했다. 그 후에 머릿속에 있는 내용을 최대한 있는 대로 정리한 후에 바텀업으로 디테일을 파고들었다. 시간상 정리하지 못한 부분도 있긴 하나 머릿속엔 있기 때문에 반쯤 성공이라고 자축해 본다.향후 목표나만의 CS 완전 정복 로드맵 마스터하기이번 워밍업 클럽은 내 첫 CS 공부였다. 이전까지는 주먹구구식으로 그때그때 필요한 정보만 공부하다 보니 지식이 계속 휘발되는 느낌이었다. 그런데 이번 CS 공부를 하면서 오직 CS 하나만을 집중적으로 공부했다 보니 왜 CS 공부를 해야 하는지 그 이유가 분명해졌다.나는 언젠가 창업을 하고자 한다. 창업해서 서비스를 운영하려면 모르긴 몰라도 아득히 높은 수준의 갖은 능력이 필요하리라 여겨진다. 그러나 요즘은 K-MOOC나 KOCW, edX 등을 통해 전공 강의조차도 무료로 들을 수 있는 시대이니 느리더라도 꾸준히 멈추지 않고 나아간다면 분명 승산이 있다고 생각한다.워밍업 클럽 마무리 관련 소감워밍업 클럽 전반 관련네트워킹이 제대로 이뤄지지 못하고 각자도생하는 느낌이어서 아쉬웠다. 워밍업 클럽이 '특정 목표를 가진 사람들을 모아놓자' 딱 거기에 의의가 있는 프로그램이라면 할 말 없지만, 운영 쪽에서 개별 스터디를 장려하는 유인(스터디 과정 제출 시 가산점, 놀러 와서 커피 쿠폰 뿌리고 가기 등)이 있었다면 훨씬 좋았을 것 같다.감자님 강의 관련 (※ 대문자 T 주의해 주세요 감자님 ㅠㅠㅠ 나쁜 마음은 없습니다ㅠㅠㅠㅠㅠㅠㅠ)자료구조 및 알고리즘 강의는 만점을 줘도 아깝지 않은 강의였다고 생각한다. 자료구조이나 알고리즘 모두 시각화가 정말 중요하다고 생각하는데, 단순히 달달 외우는 게 아니라 생각할 수 있는 힘을 기르게 해주신 훌륭한 강의였던 것 같다.운영체제 강의는 아쉬움이 남는다. (내게 이런 평가를 내릴 만큼의 지식이 있기는커녕 그 1%도 안 되지만) 내용상 잘못 설명하신 부분이나 실제로는 다르지만 같다고 퉁치고 넘어가는 내용들이 가끔 있었다. 나만 해도 지난 3주 동안 강의의 큰 오류를 2개나 찾았다. 강의 수강 대상자가 입문자라면 쉬운 설명만큼이나 내용의 정확성도 중요하다고 생각한다. 초보들은 처음 배운 지식을 수정하기가 매우 어렵기 때문이다.스터디 관련1주차부터 2주차, 그리고 오늘까지 CS 스터디를 모집해 운영해 왔다. 매주 일요일 2시에, 워밍업 클럽 일정대로 공부한 내용을 공유하는 스터디였는데 이탈률도 높고 남은 사람들의 참여율도 저조했다. 더 이상 말은 안 하겠지만... 많이 실망스러웠다.그래도 공유해야 할 내용이 있는 만큼 내가 이해한 내용을 하나라도 더 매끄럽게 설명할 수 있게 노력한다든지, 정확한 내용이 맞는지 더블, 트리플, 쿼드러플 체크를 반복했던 점은 좋았다. 혼자 공부할 때도 늘 그렇게 하고 있었다는 게 문제지만.나 자신 관련우선 나 자신에게 고생했다고 말하고 싶다. 어찌 됐건 수료를 했으니까. 얻은 것들도 무수히 많고. 무엇보다 막연하기만 했던 'CS란 뭘까?'에 대한 해답과 '비전공자인 내가 CS를 감히 도전해도 될까?'에 대한 해답을 얻은 게 긍정적이다.하지만, 개인적으로 내가 나에게 느꼈으면 하는 감정은 답답함과 공포였다. 근데 둘 모두 전혀 없다. '비전공자여도 전공자들을 찍어누를 만큼의 지식과 경험, 능력을 갖고 싶다'는 다소 허황된 꿈이 여기서 현실로 다가왔어야 했는데... 아쉽다.

알고리즘 · 자료구조알고리즘운영체제

채널톡 아이콘