블로그
전체 102025. 03. 30.
0
워밍업 클럽 3기 BE 클린코드 4주차 발자국
4주가 정말 빠르게 지나가서 벌써 스터디의 마지막 주차가 지나갔다. 가독성좋은 코드를 작성하는 것에 대해, 테스트코드에 대해 막연한 관심만 가지고, 알던 사용법과 지식 안에서 머무르던 나였는데 이번 스터디와 우빈님의 강의를 통해 지식과 경험의 확장이 시작된거 같아 만족한다. 또한 우빈님께서 매번 정말 열정적으로 중간점검 라이브를 진행해주셨는데 그 정성과 진심이 감동이었다. 강의 중 주제에 관련된 내용들 뿐만 아니라 스프링관련 이라던가 배경지식으로 필요한 여러 개념들을 설명해주시는 방식들도 좋았고 여러모로 배운 것이 정말 많은 강의였다. 추후에 우빈님의 강의도 스터디도 또 열린다면 참여할 의향이 크다. 스터디 참여를 통해서 혼자 강의를 수강했다면 느슨해져서 집중도 있게 학습하지 못했을 수 있는데, 스터디 참여를 하며 미션 수행, 회고 작성들을 기한내에 하기 위해서라도 학습에 집중도가 높아졌던 거 같고 미션들을 해결하는 과정이 학습에 많은 도움이 되었다. 또한 중간점검 라이브를 통해 강사인 우빈님께서 직접 소통하며 질의응답시간과 코드리뷰 등을 해주셔서 정말 스터디를 안할 이유가 없다고 생각한다. 이번 인프런 스터디는 값진 경험이었고 기대이상이었다. 다음에도 다른 스터디들에도 참여해보려한다. 강의 출처 :Practical Testing: 실용적인 테스트 가이드
2025. 03. 27.
0
인프런 워밍업 스터디 클럽 3기 백엔드 Day 18미션
1. @Mock, @MockBean, @Spy, @SpyBean, @InjectMocks 의 차이를 한번 정리해 봅시다. @Mock 기본적인 목(mock) 객체를 생성하고, 가짜 객체로 특정 메서드의 동작을 지정할 수 있다. 단위 테스트에서 실제 로직 대신 더미 데이터를 반환하는 데 사용한다.@MockBean Spring 컨텍스트 내에 목 객체를 빈으로 등록한다. 애플리케이션의 특정 빈을 대체하여 통합 테스트를 진행할 수 있다.@Spy 실제 객체를 생성하면서 일부 메서드만 모킹할 수 있다. 객체의 전체 동작을 유지하면서 특정 부분만 스텁으로 대체할 수 있다.@SpyBean Spring 컨텍스트에서 실제 빈을 스파이로 등록한다. 특정 Spring 빈의 일부 메서드를 테스트용으로 대체할 수 있다.@InjectMocks 테스트 대상 클래스의 인스턴스를 생성하고, 의존 객체들을 자동으로 주입한다. 외부 의존성 없이 독립적인 테스트를 가능하게 한다. 2. 아래 3개의 테스트가 있습니다. 내용을 살펴보고, 각 항목을 @BeforeEach, given절, when절에 배치한다면 어떻게 배치하고 싶으신가요? (@BeforeEach에 올라간 내용은 공통 항목으로 합칠 수 있습니다. ex. 1-1과 2-1을 하나로 합쳐서 @BeforeEach에 배치)@BeforeEach void setUp() { // 사용자 생성에 필요한 내용 준비 // 사용자 생성 // 게시물 생성에 필요한 내용 준비 // 게시물 생성 } @DisplayName("사용자가 댓글을 작성할 수 있다.") @Test void writeComment() { // given // 댓글 생성에 필요한 내용 준비 // when // 댓글 생성 // then // 댓글이 정상적으로 작성되었는지 검증 } @DisplayName("사용자가 댓글을 수정할 수 있다.") @Test void updateComment() { // given // 댓글 생성에 필요한 내용 준비 // 댓글 생성 // 댓글 수정에 필요한 내용 준비 // when // 댓글 수정 // then // 댓글이 정상적으로 수정되었는지 검증 } @DisplayName("자신이 작성한 댓글이 아니면 수정할 수 없다.") @Test void cannotUpdateCommentWhenUserIsNotWriter() { // given // 사용자1 생성 // 사용자2 생성 // 사용자1의 게시물 생성 // 사용자1의 댓글 생성 // 사용자2가 댓글 수정 시도에 필요한 내용 준비 // when // 사용자2가 댓글 수정 시도 // then // 댓글 수정이 실패했는지 검증 } 강의 출처 - Practical Testing: 실용적인 테스트 가이드
2025. 03. 25.
0
[인프런 워밍업 클럽 3기] BE 테스트 코드 - Day16 미션
레이어별 특징 & 어떻게 테스트 하면 좋을지 Persistence LayerData Access의 역할. (비즈니스 가공 로직없이 순수하게) Data CRUD에만 집중한 레이어. SQL이 제대로 동작하는지.스프링을 띄우는 통합테스트 이지만 Persistence Layer 로직만 수행되기에 단위테스트의 성격을 가진다.test용 profile 설정과 data 주입이 되도록 설정 해두면 편하다. Business LayerPersistence Layer와 상호 작용을 통해 비즈니스 로직을 수행.트랜잭션이 보장되어야 함. Persistence Layer를 주입하여 테스트를 진행.비즈니스 로직에 대해 단위 테스트를 작성해도 좋을 거 같다. Presentation Layer외부세계에서 넘어오는 값들에 대하여 최소한의 검증은 마치고 유효한 값들을 비즈니스 레이어로 넘겨줘야 한다.컨트롤러 테스트를 할때는 하위의 business layer 와 persistence layer는 모킹처리를 하고, 단위 테스트 느낌으로 진행한다.
2025. 03. 23.
0
워밍업 클럽 3기 BE 클린코드 3주차 발자국
금주 부터 본격적으로 테스트코드 강의 부분 학습을 시작하였다.강의를 들으며 개인적으로 조금 더 와닿게 느꼈거나 정리하고 싶었던 포인트들만 정리해보았다.우선 가장 첫번째, 가장 중요한 것은 WHY.귀찮고, 시간도 많이 쏟아야하는, 비용이 발생하는 테스트코드 작성. 왜 테스트를 해야하는가.(테스트 코드 없는 상황) 사람이 직접 프로덕션으로 나온 프로그램을 테스트 하면?커버할 수 없는 영역 발생경험과 감에 의존늦은 피드백유지보수 어려움소프트웨어 신뢰 down작은 프로젝트면 티가 안날 수도 있지만, 프로젝트가 커지고 기능이 늘어나고 고도화 되면 감당할 수 없게 된다.그래서 테스트 코드를 통해서 다음을 얻고자 하는것.빠른 피드백자동화안정감 TDDTDD의 장점 : 특정 케이스(해피케이스)만 검증할 가능성을 막아준다.TDD 하면 처음 부터 메서드 설계가 테스트 하기 좋게 된다.TDD는 관점의 변화를 준다.Test가 구현부 검증역할을 위한 보조 수단 ---> Test는 구현부와 상호 작용하며 발전TDD는 클라이언트 관점에서의 피드백을 느낄 수 있다. (메서드/객체를 사용하는 사람 관점에서)TDD가 만능은 아닌데, 적절한 상황에 사용할 수 있어야 한다. 그럴려면 계속 연습해보고 시도해봐야한다. 테스트는 문서다테스트 코드는 프로덕션 코드라는 공유자산에 대해서 잘 설명해주는 문서 역할도 한다. Repository 테스트 왜 해야하는가?JPA로 어떤 쿼리 날라가는지 명확히 아는데도 필요한가?제대로된 쿼리 날라가는 것 보장하기 위함미래에 어떤 형태로 변형 될지 모른다. Day11 미션 @DisplayName("가격과 할인률에 따른 총가격이 정상적으로 계산되어야 한다. 라커 사용 안하는 경우.") @ParameterizedTest @CsvSource({ "100, 0, 100", // price 100, discountRate 0, expectedTotalPrice 100 "200, 0, 200", // price 200, discountRate 0, expectedTotalPrice 200 "300, 0.1, 270", // price 300, discountRate 10, expectedTotalPrice 270 "150, 0.5, 75" // price 150, discountRate 50, expectedTotalPrice 75 }) void totalPriceTest(int price, double discountRate, int expectedTotalPrice) { // given StudyCafeSeatPass pass = StudyCafeSeatPass.of(StudyCafePassType.FIXED, 1, price, discountRate); StudyCafePassOrder order = StudyCafePassOrder.of(pass, null); // when int totalPrice = order.getTotalPrice(); // then assertThat(totalPrice).isEqualTo(expectedTotalPrice); } @DisplayName("가격과 할인률에 따른 총가격이 정상적으로 계산되어야 한다. 라커 사용 하는 경우.") @ParameterizedTest @CsvSource({ "100, 0, 10, 110", // price 100, discountRate 0, lockerPrice 10, expectedTotalPrice 110 "200, 0, 10, 210", // price 200, discountRate 0, lockerPrice 10, expectedTotalPrice 210 "300, 0.1, 10, 280", // price 300, discountRate 10, lockerPrice 10, expectedTotalPrice 280 "150, 0.5, 10, 85" // price 150, discountRate 50, lockerPrice 10, expectedTotalPrice 85 }) void totalPriceTestWithLocker(int price, double discountRate, int lockerPrice, int expectedTotalPrice) { // given StudyCafeSeatPass pass = StudyCafeSeatPass.of(StudyCafePassType.FIXED, 1, price, discountRate); StudyCafePassOrder order = StudyCafePassOrder.of(pass, StudyCafeLockerPass.of(StudyCafePassType.FIXED, 1, lockerPrice)); // when int totalPrice = order.getTotalPrice(); // then assertThat(totalPrice).isEqualTo(expectedTotalPrice); }@CsvSource 와 @ParmeterizedTest 를 사용해보았다. InputHandlerTest@DisplayName("사용자 입력값에 따라 알맞은 이용권이 선택되어야 한다.") @Test void testGetPassTypeSelectingUserAction_HOURLY() { //given InputHandler inputHandler = new InputHandler(); String input = "1\n"; System.setIn(new ByteArrayInputStream(input.getBytes())); // when StudyCafePassType passType = inputHandler.getPassTypeSelectingUserAction(); // then assertThat(passType).isEqualTo(StudyCafePassType.HOURLY); } @DisplayName("사용자 입력값에 따라 알맞은 이용권이 선택되어야 한다.") @Test void testGetPassTypeSelectingUserAction_WEEKLY() { //given String input = "2\n"; System.setIn(new ByteArrayInputStream(input.getBytes())); InputHandler inputHandler = new InputHandler(); // when StudyCafePassType passType = inputHandler.getPassTypeSelectingUserAction(); // then assertThat(passType).isEqualTo(StudyCafePassType.WEEKLY); } @DisplayName("사용자 입력값에 따라 알맞은 이용권이 선택되어야 한다.") @Test void testGetPassTypeSelectingUserAction_FIXED() { //given String input = "3\n"; System.setIn(new ByteArrayInputStream(input.getBytes())); InputHandler inputHandler = new InputHandler(); // when StudyCafePassType passType = inputHandler.getPassTypeSelectingUserAction(); // then assertThat(passType).isEqualTo(StudyCafePassType.FIXED); } @DisplayName("사용자 입력값에 따라 알맞은 이용권이 선택되어야 한다.") @Test void testGetPassTypeSelectingUserAction_INVALID() { //given String input = "4\n"; System.setIn(new ByteArrayInputStream(input.getBytes())); InputHandler inputHandler = new InputHandler(); // when then assertThatThrownBy(inputHandler::getPassTypeSelectingUserAction) .isInstanceOf(AppException.class); }InputHandler에 static으로 선언되어 있는 Scanner를 공유한다. 위의 네개의 테스트를 각각 실행하면 성공한다. 하지만 System.setIn을 사용해 입력 스트림을 변경하고 Scanner가 이 스트림을 사용Scanner는 입력 스트림을 읽을 때, 스트림을 소모. 한 번 사용한 스트림은 소모되므로, 같은 스트림을 여러 테스트에서 공유하면 이후 테스트에서 입력을 읽어들이지 못해 실패따라서, 각 테스트에서 새로운 입력 스트림을 주입하더라도, 이전 테스트에서 스트림을 사용해버렸기 때문에 이후 테스트들은 스트림을 사용할 수 없게 되는 문제가 발생 하여 순차적으로 실행하면 첫번째로 실행되는 테스트만 통과되고 그 이후 테스트 들에서는 오류 발생으로 실패한다. 여러 다른 방법들을 찾아봤지만, 결국은 이 부분을 테스트 할 수 있게 만들려면 InputHandler가 scanner를 주입받도록 구조를 바꿔야 하는 거 같았다. 그런데 테스트를 위해 구조를 바꾸는 것이 타당한가 생각이 들고 테스트를 위해 프로덕션 코드에 뭔가를 추가하거나 수정하는게 어느 정도 선까지 괜찮은 것인지 기준을 모르겠다. 이 부분을 학습해봐야겠다. 자료, 강의 출처 : 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
2025. 03. 23.
2
[인프런 워밍업 클럽 3기] CS - 3주차 미션 & 발자국
운영체제1. 메모리의 종류는 어떤것들이 있나요? 각 메모리의 특징도 함께 적어주세요. 레지스터가장 빠른 메모리CPU 내에 존재휘발성 메모리 (컴퓨터 꺼지면 데이터 사라짐)CPU 구분시 말하는 32/64bit 는 레지스터의 크기를 말하는 것.CPU는 계산시 메인메모리 (RAM)에 있는 값을 레지스터로 가져와서 계산하고 그 결과를 다시 메인메모리에 저장시킴 메인메모리레지스터와 메인메모리 사이에는 휘발성 메모리인 캐시 가 있음.메인메모리는 너무 느리기 때문에 필요할 것 같은 데이터를 미리 캐시로 가져옴.캐시는 성능의 이유로 여러개를 둠.만약 CPU가 값을 요청해 레지스터로 옮겨야 할 때 L1캐시(가장빠름) -> L2캐시 (L3..) -> 메인메모리순서로 값이 있는지 확인함.===메인 메모리는 실제 운영체제와 다른 프로세스들이 올라가는 공간.전원 공급 안되면 데이터가 지워지기에 휘발성 메모리HDD, SDD 보다 속도가 빠르지만 비싸기 때문에 데이터 저장보단 실행 중인 프로그램만 올림. 보조저장장치 (HDD, SSD)비휘발성 메모리.가격이 쌈. 2. 사용자 프로세스가 메모리의 운영체제 영역에 침범하지 못하도록 만든 레지스터는 어떤 레지스터일까요?경계 레지스터. 메모리 관리자가 사용자 프로세스가 경계 레지스터의 값을 벗어났는지 감시하고, 만약 벗어난다면 해당 프로세스를 종료시킨다. 3. 메모리 할당 방식에서 가변 분할 방식과 고정 분할 방식의 장단점은 뭔가요?가변 분할 방식장점 : 메모리의 연속된 공간에 할당 되기에 더 크게 할당되어 낭비되는 공간인 내부 단편화가 적다.단점 : 한 프로세스가 분산되어 할당되므로 메모리 공간이 남아있어도 추가적 할당을 할 수 없는 외부 다편화가 발생한다. 고정 분할 방식장점 : 구현이 간단하고 오버헤드가 적다.단점 : 한 프로세스보다 더 큰 메모리가 할당 될 수 있고 이로 인해 특정 메모리보다 더 큰 공간이 할당되는 내부 단편화가 발생할 수 있다. 4. CPU 사용률을 올리기 위해 멀티프로그래밍을 올렸지만 스왑이 더 많이 이루어져 CPU 사용률이 0%에 가까워 지는 것을 뭐라고 할까요?스레싱.물리 메모리의 크기가 부족해서 일어나는 현상. SWAP시의 컨텍스트 스위칭으로 인해 발생한다.5. HDD나 SSD는 컴퓨터를 실행시키는데 꼭 필요한 걸까요? 이유를 함께 적어주세요.HDD나 SSD가 "꼭" 필요한 건 아니다. 운영체제를 ROM, USB 또는 외부 저장장치에서 부팅하면 HDD나 SSD 없이도 컴퓨터를 실행할 수 있다. 하지만 대부분의 경우 컴퓨터가 안정적으로 동작하려면 운영체제를 저장할 영구 저장장치가 필요해서, 일반적으로 HDD나 SSD가 필수로 여겨진다.메모리는 휘발성이라 전원이 꺼지면 데이터가 사라진다. 그래서 데이터를 영구적으로 저장할 수 있는 HDD나 SSD 같은 장치가 중요하다. 6. 파일을 삭제해도 포렌식으로 파일을 복구할 수 있는 이유가 무엇일까요?OS의 파일 시스템에는 파일 관리를 위해 빈 공간을 모아 두는 Free block list가 있다. 특정 파일을 삭제하면, 파일 시스템은 파일의 모든 정보를 지우는 대신 파일 테이블의 헤더만 삭제하고, 그 파일이 차지하던 블록을 free block list에 추가한다.이렇게 되면 사용자 입장에서는 파일이 삭제된 것처럼 보인다. 하지만 실제로는 사용된 블록의 데이터가 그대로 남아 있기 때문에, 이후 포렌식을 통해 해당 데이터를 복구할 수 있다. 자료구조와 알고리즘1. 지금까지 배운 5개의 정렬 알고리즘의 장단점과 시간 복잡도를 적어주세요. 버블 정렬시간 복잡도: O(n²)장점: 구현이 쉽고 안정 정렬단점: 매우 비효율적 선택 정렬시간 복잡도: O(n²)장점: 데이터 이동 횟수 최소단점: 안정 정렬이 아님, 느림 삽입 정렬시간 복잡도: O(n²)장점: 거의 정렬된 데이터에 빠름 (O(n))단점: 역순이면 O(n²) 병합 정렬시간 복잡도: O(n log n)장점: 항상 빠르고 안정 정렬단점: 추가 메모리 필요 (O(n)) 퀵 정렬시간 복잡도: O(n log n) (최악 O(n²))장점: 평균적으로 가장 빠름, 메모리 적게 사용단점: 최악의 경우 O(n²) 2. 메모리가 부족한 시스템에서 어떤 문제를 해결하는데 재귀로 쉽게 구현이 가능할 것 같습니다. 여러분이라면 메모이제이션과 타뷸레이션 중 어떤 걸 이용하실 건가요? 이유를 함께 적어주세요. 재귀 호출이 과도하게 깊어지면 스택 메모리 한계를 초과하는 스택 오버플로우가 발생할 수 있습니다. 이 문제를 해결하기 위해 타뷸레이션 방식을 활용하면 반복문과 배열을 사용해 중간 계산 결과를 저장함으로써 메모리를 더 효율적으로 관리하고 성능도 향상시킬 수 있습니다. 짧은 기간이었지만 이번 워밍업 클럽 스터디에 참가하면서 미뤄왔던 CS 학습을 시작하고 집중도 있게 할 수 있었다. 쉽고 재밌게 잘 설명해주시는 강의 덕분에 부담감 적게 흥미를 유지하며 학습해나갈 수 있었고, 강사님께서 직접 내주시는 미션을 수행하며 더 효과적인 학습을 할 수 있었다. 이번 학습을 시작으로 앞으로도 꾸준히 흥미를 갖고 CS 관련 학습을 이어가야겠다.
2025. 03. 16.
0
워밍업 클럽 3기 BE 클린코드 2주차 발자국
이번주는 중간점검 시간에 우빈님의 러너분들 코드리뷰시간이 있었다. 나는 원하는 만큼 충분히 리팩토링을 진행하지 못해서 코드리뷰 신청을 하진 않았지만 다른 분들의 코드리뷰 내용을 보며 정말 많은 인사이트를 얻을 수 있었다. 무려 두시간 가까이 라이브를 진행해주신 우빈님께 정말 감사드린다. 코드리뷰를 받은 러너분 중 나와 유사한 코드를 작성하신 분이 계셨다. 카페이용권 Enum의 필드로 입력값 (1, 2, 3)을 두고 관리하는 코드를 작성 하였는데, 사실 코드를 작성 하면서도 긴가민가 했다.이 부분에 대해 우빈님께서 input과 관련된 내용에 대해 가장 핵심적인 model이 의존성을 가지면 안된다고 하셨다.지금은 1, 2, 3 의 입력값으로 받지만 a, b, c 등 바뀔 수 있고, 바뀐 경우 input과 관련된 사항을 model에 가서 바꾸는 건 이상한 것이다. FileHandler 부분에서도 나와 유사한 경우가 있었다. 코드 리뷰를 받으신 분은 FileHandler가 사용되는 클래스에 상수로 이를 선언하여 초기에 파일을 모두 읽어 오도록 작성하셨다.나는 FileHandler 생성자 내에 파일을 불러오는 메서드를 두어 객체 생성시 파일을 모두 읽어 오도록 하였다.우빈님께서 초기에 파일을 모두 불러온다는 접근은 좋은데, 이런 경우 프로그램 실행 중 해당 정보를 어떻게 생성/수정/삭제 할 것인지 추가적인 고민이 필요하다고 하셨다. 코드리뷰 외에 강의 내용 중 내 기존 사고를 벗어나게 된 기억에 남는 점은 추상화 수준을 맞추는 것이 메서드 분리의 기준이 되기에 아무리 작은 코드량이라도 메서드 분리를 할 수 있다는 것이었다.depth 줄이기는 보이는 depth 줄이기가 절대 진리가 아니고 보다 중요한건 사고의 depth 줄이기! 2주가 정말 빠르게 지나갔는데 처음 마음 만큼 더 열심히 하지 못했다는 아쉬움이 남는다. 벌써 절반이 지났으니, 남은 절반은 후회 없이 열심히 해야겠다.
2025. 03. 16.
0
[인프런 워밍업 클럽 3기] CS - 2주차 미션
운영체제 FIFO 스케줄링의 장단점이 뭔가요?장점: 구현이 간단, 공정한 실행 순서, 기아 현상 없음단점: 평균 대기 시간이 길어질 수 있음, 짧은 프로세스가 긴 프로세스 뒤에서 대기 SJF를 사용하기 여러운 이유가 뭔가요?프로세스의 실행 시간을 미리 정확히 예측하기 어려움예측 오류로 최적화 효과 감소기아 현상 발생 가능성RR 스케줄링에서 타임 슬라이스가 아주 작으면 어떤 문제가 발생할까요?문맥 교환(context switch) 오버헤드 증가CPU 효율성 저하프로세스 실행보다 스케줄링에 더 많은 시간 소비운영체제가 MLFQ에서 CPU Bound Process와 I/O Bound Process를 어떻게 구분할까요?운영체제는 프로세스가 얼마나 오랫동안 CPU를 사용하는지를 기반으로 CPU Bound와 I/O Bound 프로세스를 구분하고, I/O Bound 프로세스는 상위 우선순위로, CPU Bound 프로세스는 하위 우선순위로 배치공유자원이란무엇인가요?여러 프로세스나 스레드가 동시에 접근하여 사용할 수 있는 자원메모리, 파일, 데이터베이스, 입출력 장치 등교착상태에 빠질 수 있는 조건은 어떤 것들을 충족해야할까요?상호 배제: 자원을 한 번에 한 프로세스만 점유.점유 및 대기: 자원을 점유한 상태에서 다른 자원을 기다림.비선점: 자원을 강제로 빼앗을 수 없음.순환 대기: 프로세스들이 자원을 순환적으로 기다림. 자료구조와 알고리즘재귀함수에서 기저조건을 만들지 않거나 잘못 설정했을 때 어떤 문제가 발생할 수 있나요?0부터 입력 n까지 홀수의 합을 더하는 재귀 함수를 만들어보세요.function sumOdd(n){ // 재귀 로직 } console.log(sumOdd(10)) // 25 1 ) 무한 재귀 호출이 발생하여 스택 메모리가 넘쳐서 Stack Overflow 에러가 발생할 수 있습니다. 재귀 함수가 종료되지 않고 계속해서 자신을 호출하는 문제가 생깁니다. 2) function sumOdd(n) { if (n 다음 코드는 매개변수로 주어진 파일 경로(.는 현재 디렉토리)에 있는 하위 모든 파일과 디렉토리를 출력하는 코드입니다. 다음 코드를 재귀 함수를 이용하는 코드로 변경해보세요.변경 이전 const fs = require("fs"); // 파일을 이용하는 모듈 const path = require("path"); // 폴더와 파일의 경로를 지정해주는 모듈 function traverseDirectory1(directory){ const stack = [directory]; // 순회해야 할 디렉토리를 저장할 스택 while (stack.length > 0) { // 스택이 빌 때까지 반복 const currentDir = stack.pop(); // 현재 디렉토리 const files = fs.readdirSync(currentDir); // 인자로 주어진 경로의 디렉토리에 있는 파일or디렉토리들 for (const file of files) { // 현재 디렉토리의 모든 파일or디렉토리 순회 const filePath = path.join(currentDir, file); //directory와 file을 하나의 경로로 합쳐줌 const fileStatus= fs.statSync(filePath); // 파일정보 얻기 if (fileStatus.isDirectory()) { // 해당 파일이 디렉토리라면 console.log('디렉토리:', filePath); stack.push(filePath); } else { // 해당 파일이 파일이라면 console.log('파일:', filePath); } } } } traverseDirectory1("."); // 현재 경로의 모든 하위 경로의 파일, 디렉토리 출력 변경 이후function traverseDirectory(directory) { const files = fs.readdirSync(directory); // 인자로 주어진 경로의 디렉토리에 있는 파일or디렉토리들 for (const file of files) { const filePath = path.join(directory, file); //directory와 file을 하나의 경로로 합쳐줌 const fileStatus = fs.statSync(filePath); // 파일정보 얻기 if (fileStatus.isDirectory()) { // 디렉토리라면 console.log('디렉토리:', filePath); traverseDirectory(filePath); // 재귀 호출로 하위 디렉토리 순회 } else { // 파일이라면 console.log('파일:', filePath); } } } traverseDirectory("."); // 현재 경로의 모든 하위 경로의 파일, 디렉토리 출력
2025. 03. 16.
0
[인프런 워밍업 클럽 스터디 3기] CS - 2주차 발자국
2주차 학습 내용 정리 운영체제프로세스 동기화프로세스는 독립적으로 실행되기도 하지만, 다른 프로세스와 데이터를 주고받으며 통신하는 경우도 있다. 프로세스 간 통신은 여러 방식으로 이루어질 수 있다.프로세스 간 통신 방법한 컴퓨터 내에서의 통신파일을 이용한 방법: 여러 프로세스가 하나의 파일을 읽고 쓰며 통신파이프를 이용한 방법: 운영체제가 생성한 파이프를 통해 데이터 교환쓰레드를 이용한 방법: 한 프로세스 내 쓰레드들은 코드, 데이터, 힙 영역을 공유하므로 전역변수나 힙을 통해 통신 가능네트워크를 이용한 통신소켓 통신: 운영체제가 제공하는 소켓을 이용한 통신RPC(원격 프로시저 호출): 다른 컴퓨터에 있는 함수를 호출하는 방식공유자원과 동기화 문제프로세스 간 통신 시 공동으로 이용하는 변수나 파일을 공유자원이라고 한다. 여러 프로세스가 공유자원에 접근할 때 각 프로세스의 접근 순서에 따라 결과가 달라질 수 있다.컨텍스트 스위칭으로 인한 시분할 처리 때문에 어떤 프로세스가 먼저 실행될지 예측하기 어렵고, 이로 인해 연산 결과를 예측하기 힘든 동기화 문제가 발생한다.임계구역과 상호배제임계구역(Critical Section): 여러 프로세스가 동시에 사용하면 안 되는 영역상호배제(Mutual Exclusion) 메커니즘의 요구사항:임계영역에는 동시에 하나의 프로세스만 접근해야 함여러 요청이 있어도 하나의 프로세스만 접근 허용임계구역에 들어간 프로세스는 빠르게 나와야 함동기화 해결 방법세마포어(Semaphore)상호배제 메커니즘의 한 종류정수형 변수로 구현됨공유 자원의 개수에 따라 초기값 설정wait() 함수로 시작하고 signal() 함수로 종료단점: wait()와 signal() 함수를 잘못 사용할 경우 문제 발생모니터(Monitor)세마포어의 단점을 해결한 상호배제 메커니즘프로그래밍 언어 차원에서 지원하는 방법자바의 synchronized 키워드가 대표적 예시데드락(교착상태)데드락은 여러 프로세스가 서로 다른 프로세스의 작업이 끝나기를 기다리다가 아무도 작업을 진행하지 못하는 상태를 말한다.교착상태의 필요조건교착상태가 발생하기 위해서는 다음 4가지 조건이 모두 충족되어야 한다.상호배제: 한 프로세스가 자원을 점유하면 다른 프로세스와 공유할 수 없음비선점: 프로세스가 자원을 빼앗길 수 없음점유와 대기: 프로세스가 자원을 가진 상태에서 다른 자원을 기다림원형 대기: 점유와 대기 관계가 원형을 이룸데드락 해결 방법교착상태 회피(Deadlock avoidance)자원 할당 시 교착상태가 발생하지 않는 수준의 자원만 할당안정 상태(Safe state)와 불안정 상태(Unsafe state)로 구분은행원 알고리즘: 전체 자원과 프로세스의 최대요구자원을 고려해 자원 할당교착상태 검출 및 해결가벼운 교착상태 검출: 타이머를 이용일정 시간 동안 프로세스가 작업을 진행하지 않으면 교착상태로 간주체크포인트로 롤백하여 해결무거운 교착상태 검출: 자원 할당 그래프 이용프로세스들 간 자원 점유와 대기 관계를 그래프로 표현순환구조가 생기면 교착상태로 판단교착상태를 일으킨 프로세스를 강제종료하고 체크포인트로 롤백프로그래밍 언어와 프로세스프로그래밍 언어 유형컴파일 언어개발자가 작성한 코드를 컴파일 과정을 거쳐 기계어로 변환컴파일 과정에서 문법 오류 검사실행 속도가 빠름C, C++, C# 등이 해당인터프리터 언어코드를 한 줄씩 해석해 실행실행 시 오류가 발생할 수 있음컴파일 언어보다 속도가 느림JavaScript, Python, Ruby 등이 해당코드 실행 방식컴파일 방식전체 코드를 기계어로 변환 후 실행실행 전 타입/문법 검사로 안정성 확보빠른 실행 속도 (C, C++, Java 등)처리 과정: 코드 → 전처리기 → 컴파일러 → 어셈블러 → 링커 → 실행파일인터프리터 방식코드를 실행 시점에 한 줄씩 해석별도의 컴파일 과정 없음실행 속도는 상대적으로 느림 (JavaScript, Python, Ruby 등)프로세스의 메모리 구조프로세스는 네 가지 메모리 영역으로 구성된다.코드 영역: 실행할 코드가 저장된 영역데이터 영역: 전역변수나 배열이 저장되는 영역스택 영역: 지역변수, 함수 매개변수, 함수의 리턴 주소 등이 저장힙 영역: 동적 메모리 할당이 이루어지는 영역프로그램 실행 과정사용자가 프로그램 실행 → 운영체제가 프로세스 생성exe 파일의 코드와 데이터 영역을 프로세스의 해당 영역에 로드빈 스택과 힙 영역 할당PCB(Process Control Block) 생성하여 관리프로그램 카운터를 코드 영역의 첫 주소로 설정CPU 스케줄링에 따라 프로세스 실행메모리메모리의 종류와 특징레지스터가장 빠른 기억장소로 CPU 내에 존재휘발성 메모리CPU 비트(32비트/64비트)를 결정계산 시 메인메모리 값을 레지스터로 가져와 처리캐시레지스터와 메인메모리 사이의 중간 매개체필요할 것 같은 데이터를 미리 가져와 저장여러 레벨로 구성(L1, L2, L3)메인메모리(RAM)운영체제와 실행 중인 프로그램이 올라가는 공간휘발성 메모리HDD/SSD보다 빠르지만 비싸서 실행 중인 프로그램만 저장보조저장장치(HDD, SSD)비휘발성 메모리가격이 저렴하고 용량이 큼데이터 영구 저장에 적합메모리 주소물리 주소: 실제 메모리의 위치(하드웨어 관점)논리 주소: 사용자 관점에서의 주소절대 주소: 메모리 관리자가 바라본 실제 프로그램 위치상대 주소: 컴파일러가 0번지로 가정한 주소메모리 관리 방식유니프로그래밍 환경메모리 오버레이: 큰 프로그램을 작게 나눠 일부만 실행멀티프로그래밍 환경가변 분할 방식프로세스 크기에 따라 메모리 할당장점: 내부 단편화가 없음단점: 외부 단편화 발생고정 분할 방식정해진 크기로 메모리 분할장점: 구현이 간단하고 오버헤드가 적음단점: 내부 단편화 발생버디 시스템2의 승수로 메모리 분할장점: 가변/고정 분할 방식의 단점 최소화외부 단편화 방지에 용이하며, 내부 단편화도 최소화자료구조와 알고리즘재귀(Recursion)재귀는 어떠한 것을 정의할 때 자기 자신을 참조하는 방식이다. 재귀 함수는 자기 자신을 호출하는 함수로, 반드시 탈출조건(기저조건)이 있어야 한다.재귀 함수의 특징콜스택에 함수 호출이 쌓임(FILO 구조)탈출조건이 없으면 스택 오버플로우 발생구현 시 자기 자신이 이미 구현되어 있다고 가정하면 쉽게 작성 가능재귀적 사고 패턴단순 반복 실행: 성능은 for문보다 떨어짐하위 문제 결과 기반 계산: 팩토리얼 같은 문제에 적합하향식 계산: 재귀를 통해 큰 문제를 작은 문제로 분할하여 해결상향식 계산: for문이나 재귀로 작은 문제부터 해결하여 큰 문제 해결하노이 탑재귀 함수의 대표적인 예시로, 하향식 계산의 좋은 사례입니다.세 개의 기둥과 크기가 다른 원반들로 구성규칙: 한 번에 하나의 원반만 이동, 작은 원반 위에 큰 원반이 올 수 없음하위 문제로 분할하여 해결정렬 알고리즘버블 정렬(Bubble Sort)가장 간단한 정렬 알고리즘 중 하나로, 인접한 두 요소를 비교하여 정렬한다.특징구현이 매우 간단함성능은 O(n²)으로 좋지 않음매 반복마다 가장 큰 요소가 마지막으로 이동선택 정렬(Selection Sort)정렬되지 않은 영역에서 최솟값(또는 최댓값)을 찾아 정렬된 영역의 끝에 배치하는 알고리즘이다.특징구현이 간단함성능은 O(n²)으로 좋지 않음정렬된 영역과 정렬되지 않은 영역으로 구분하여 처리두 정렬 알고리즘 모두 이해와 구현은 쉽지만, 대용량 데이터나 성능이 중요한 경우에는 더 효율적인 정렬 알고리즘을 사용하는 것이 좋다. 벌써 2주차가 지나갔다. 이번주에 강의를 몰아듣게 되어 학습 후에 머리속에 정리가 되지 않았는데 발자국을 작성하며 학습정리에 도움이 된거 같다. 남은 기간은 미리 강의를 듣고 미리 잘 정리해두도록 해야겠다. 참고 출처https://www.inflearn.com/course/%EC%9E%90%EB%A3%8C%EA%B5%AC%EC%A1%B0-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%EA%B8%B0%EB%B3%B8https://www.inflearn.com/course/%EB%B9%84%EC%A0%84%EA%B3%B5%EC%9E%90-%EC%9A%B4%EC%98%81%EC%B2%B4%EC%A0%9C
2025. 03. 09.
0
[인프런 워밍업 클럽 스터디 3기] CS - 1주차 발자국
1주차 학습내용 운영체제운영체제폴링 방식: CPU가 주기적으로 I/O 장치를 확인하여 작업 완료 여부를 확인하는 방식.인터럽트 방식: I/O 작업이 완료되면 장치가 CPU에게 신호를 보내 작업 완료를 알리는 방식. CPU는 인터럽트를 받아 ISR을 실행함.프로세스: 하드디스크에 저장된 프로그램이 메모리에 올라와 실행 중인 상태.프로세스 구조: 코드 영역, 데이터 영역, 힙 영역, 스택 영역으로 구분.컴파일 과정: C 언어 코드는 전처리 -> 컴파일 -> 링킹을 거쳐 실행 파일로 변환됨.프로그래밍과 프로세싱유니 프로그래밍: 하나의 프로세스만 메모리에 올라가며, I/O 작업 중 CPU가 대기하는 방식.멀티 프로그래밍: 메모리에 여러 프로세스를 올려, I/O 작업 동안 다른 프로세스를 실행함.멀티 태스킹: CPU가 여러 프로세스를 짧은 시간 동안 교대로 처리하는 방식.멀티 프로세싱: 여러 CPU를 사용하여 작업을 처리하는 방식.PCB프로세스 제어 블록: 각 프로세스의 정보를 저장하는 자료 구조.구조: 프로세스 ID, 상태, 프로그램 카운터, 레지스터 정보 등을 포함.프로세스 상태: 생성(NEW) -> 준비(READY) -> 실행(RUNNING) -> 대기(WAITING) -> 종료(TERMINATED).컨텍스트 스위칭정의: 실행 중인 프로세스의 상태를 저장하고 다른 프로세스의 상태로 교체하는 작업.스위칭 이유: CPU 점유 시간 초과, I/O 요청, 인터럽트 발생 등.스레드정의: 프로세스 내에서 실행되는 가장 작은 작업 단위.공유 자원: 스레드는 같은 프로세스 내에서 코드, 데이터, 힙 영역을 공유함.CPU 스케줄링FIFO: 먼저 들어온 프로세스를 먼저 처리하는 방식.SJF: 실행 시간이 짧은 프로세스를 우선 처리하는 방식.RR: 각 프로세스에 일정 시간씩 CPU를 할당하는 방식.MLFQ: 우선순위에 따라 다중 큐를 사용하는 방식. 짧은 작업은 낮은 타임 슬라이스를, 긴 작업은 긴 타임 슬라이스를 부여함. 자료구조와 알고리즘자료구조데이터를 효율적으로 저장하고 관리하는 방법으로, 다양한 형태의 데이터 처리를 가능하게 함. 예를 들어 배열, 연결 리스트 등이 있음.배열: 메모리상에서 연속적으로 데이터를 저장하는 구조로, 인덱스를 통해 빠르게 접근 가능함.연결 리스트: 각 요소가 다음 요소의 주소를 저장해 연결된 구조로, 삽입과 삭제가 용이하지만, 특정 요소에 접근할 때는 순차 탐색이 필요함.알고리즘문제를 해결하기 위한 명확한 절차나 규칙의 집합. 자료구조와 결합하여 다양한 문제 해결을 가능하게 함.시간 복잡도: 알고리즘의 성능을 측정하는 척도로, 입력 크기에 따라 알고리즘이 실행되는 데 걸리는 시간을 빅오(Big-O) 표기법으로 표현함. 예: O(n), O(log n). 스택: LIFO(Last In First Out) 방식으로, 마지막에 삽입된 데이터가 먼저 처리되는 구조. 호출 스택, 되돌리기 기능 등에 사용됨.큐: FIFO(First In First Out) 방식으로, 먼저 삽입된 데이터가 먼저 처리되는 구조. 작업 큐나 대기열 관리에 사용됨.덱(Deque): 양쪽 끝에서 삽입과 삭제가 가능한 자료구조로, 스택과 큐의 기능을 모두 갖춘 구조.해시테이블키를 기반으로 값을 빠르게 찾을 수 있는 자료구조. 해시 함수로 키를 계산해 데이터를 저장하고 검색함.평균적으로 빠른 데이터 접근 속도를 제공하지만, 해시 충돌이나 메모리 사용량 문제가 발생할 수 있음.Set중복된 데이터를 허용하지 않는 자료구조로, 각 요소가 유일한 값을 가짐.중복을 제거하거나 유일한 항목만 저장하는 경우에 자주 사용됨. 1주차 미션운영체제 while(true){ wait(1); // 1초 멈춤 bool isActivated = checkSkillActivated(); // 체크 } 위 코드는 1초 마다 플레이어가 스킬을 사용했는지 체크하는 코드입니다. 이 방식은 폴링방식입니다. 1초마다 체크하기 때문에 성능에 좋지 않습니다. 이를 해결하기 위한 방식으로 어떤 걸 이용해야 할까요? 현재 코드는 1초마다 checkSkillActivated() 함수를 호출해 스킬 사용 여부를 확인하는 폴링 방식입니다. 이 방식은 일정 주기로 계속 CPU를 사용하기 때문에 리소스 낭비가 발생할 수 있습니다.이를 해결하기 위해 인터럽트 방식을 사용할 수 있습니다. 인터럽트 방식에서는 스킬이 사용된 시점에만 인터럽트 신호가 발생하고, CPU는 이 신호를 받아 즉시 인터럽트 서비스 루틴을 실행하여 필요한 작업을 처리합니다. 이렇게 하면 CPU는 스킬이 사용될 때만 작업을 처리하므로, 불필요한 주기적인 체크를 없애고 성능을 향상시킬 수 있습니다. 2.프로그램과 프로세스가 어떻게 다른가요?프로그램은 정적인 개념으로, 디스크와 같은 보조 기억 장치에 저장된 실행되지 않은 코드의 집합입니다. 이 파일 자체는 CPU에서 실행되지 않으며, 단지 명령어들이 기록된 상태입니다. 반면, 프로세스는 동적인 개념으로, 실행 중인 프로그램을 의미합니다. 프로세스는 메모리에 로드되어 CPU에 의해 실제로 실행되고 있는 상태이며, 이를 통해 메모리, CPU 스케줄링 등의 자원을 할당받아 작업을 수행합니다. 즉, 프로그램은 실행되지 않은 코드이고, 프로세스는 실행 중인 프로그램입니다. 멀티프로그래밍과 멀티프로세싱이 어떻게 다른가요?멀티프로그래밍은 단일 CPU 환경에서 여러 프로그램이 동시에 메모리에 적재되어 번갈아가며 실행되는 방식입니다. 이 방식은 CPU가 효율적으로 작업을 처리할 수 있도록 다양한 프로세스를 동시에 실행할 수 있게 하지만, 한 번에 하나의 프로세스만 CPU에서 실제로 실행됩니다. 반면, 멀티프로세싱은 다중 CPU 환경을 사용하는 방식으로, 여러 CPU 코어가 동시에 여러 프로세스를 병렬로 실행할 수 있습니다. 이를 통해 시스템은 더 높은 처리 성능을 발휘하게 됩니다. 운영체제는 프로세스를 관리하기 위해서 어떤 것을 사용하나요?운영체제는 PCB (Process Control Block)이라는 구조체를 사용해 각 프로세스를 관리합니다. PCB는 프로세스에 관한 다양한 정보를 포함하고 있으며, 프로세스의 상태, 프로세스 ID, 프로그램 카운터, CPU 레지스터 값, 메모리 할당 정보 등을 저장합니다. PCB는 각 프로세스가 CPU에서 실행되는 동안의 문맥(context)을 저장하고, 프로세스가 CPU를 할당받을 때 이전의 상태를 복원하는 데 사용됩니다. 프로세스가 생성되거나 종료될 때 PCB가 추가되거나 제거되며, PCB 간의 관계는 연결 리스트와 같은 자료구조로 관리됩니다. 컨텍스트 스위칭이란 뭔가요?컨텍스트 스위칭은 CPU가 한 프로세스에서 다른 프로세스로 작업을 전환할 때 발생하는 과정입니다. 현재 실행 중인 프로세스의 상태(프로그램 카운터, CPU 레지스터 값 등)를 PCB에 저장한 후, 다음으로 실행할 프로세스의 상태를 PCB에서 가져와 복원하는 과정입니다. 이로 인해 CPU는 새로운 프로세스를 계속해서 실행할 수 있습니다. 컨텍스트 스위칭이 자주 발생하면 시스템 오버헤드가 증가해 성능 저하를 유발할 수 있지만, 이는 멀티태스킹 환경에서 필수적인 과정입니다. 자료구조와 알고리즘여러분은 교실의 학생 정보를 저장하고 열람할 수 있는 관리 프로그램을 개발하려고 합니다. 이 때 여러분이라면 학생의 정보를 저장하기 위한 자료구조를 어떤 걸 선택하실 건가요? 이유를 함께 적어주세요. 저는 HashMap을 사용할 것 같습니다. 이유는 학생의 정보는 학번이나 이름과 같이 고유한 식별자를 기반으로 관리되는 경우가 많기 때문에, 이러한 고유 식별자를 키로 사용하는 HashMap은 효율적입니다. HashMap은 평균적으로 O(1)의 시간복잡도로 데이터를 삽입하고 검색할 수 있기 때문에, 대규모 학생 정보를 빠르게 처리하는 데 적합합니다. 여러분은 고객의 주문을 받는 프로그램을 개발하려고 합니다. 주문은 들어온 순서대로 처리됩니다. 이 때 여러분이라면 어떤 자료구조를 선택하실 건가요? 이유를 함께 적어주세요.Queue 자료구조를 선택하겠습니다. 주문을 처리할 때는 FIFO(First-In-First-Out) 방식이 적합합니다. 큐는 먼저 들어온 데이터가 먼저 처리되는 방식으로, 주문이 들어온 순서대로 처리를 보장합니다. 우리가 구현한 스택은 0번 인덱스, 즉 입구쪽으로 데이터가 삽입되고 나오는 구조입니다. 반대로 마지막 인덱스, 즉 출구쪽으로 데이터가 삽입되고 나오는 구조로 코드를 변경해주세요.import { LinkedList } from './LinkedList.mjs'; class Stack { constructor() { this.list = new LinkedList(); } push(data) { this.list.insertLast(data); // 마지막 인덱스에 삽입 } pop() { try { return this.list.deleteLast(); // 마지막 인덱스에서 삭제 } catch (e) { return null; } } peek() { return this.list.getNodeAt(this.list.count - 1); // 마지막 인덱스의 데이터 확인 } isEmpty() { return this.list.count === 0; } } export { Stack }; 해시테이블의 성능은 해시 함수에 따라 달라집니다. 수업 시간에 등번호를 이용해 간단한 해시 함수를 만들어봤습니다. 이번엔 등번호가 아닌 이름을 이용해 데이터를 골고루 분산시키는 코드로 수정해주세요. 힌트: charCodeAt() 함수를 이용 예시: name1 = "이운재"; name1.charCodeAt(0); // 51060 이운재의 0번 인덱스 ‘이’의 유니코드 출력이름의 각 문자의 ASCII 값을 이용하여 해시값을 계산하고, 이를 해시 테이블의 크기로 나눈 나머지를 반환하여 분산도를 높이는 해시 함수를 작성할 수 있습니다.public class HashUtil { public int hashFunction(String name, int tableSize) { int hashValue = 0; for (int i = 0; i 1주차 미션 회고이번주에 너무 많은 스케줄들이 몰려서 힘겹게 학습을 따라가느라 버거웠다. 복습도 필요할 거 같고, 다음 주에는 지금의 아쉬운 마음을 동력 삼아서 더 열심히 해나가야겠다.
2025. 03. 09.
0
인프런 워밍업 클럽 스터디 3기 - 백엔드 클린 코드, 테스트 코드 1주차 발자국
이번주차 학습 부분 [섹션1 - 5]지금까지 나는 클린코드의 원칙들에 대해 왜 이러한 원칙을 지키는 것이 좋은지 깊게 생각해보지 못했고, 가독성 좋은 코드를 작성하고 싶다는 생각이 있었음에도 구체적이고 체계적으로 스스로 생각을 정리해보거나 학습해보지 못했던거 같다. 이번 주차 학습을 진행하며 이러한 생각들이 많이 정리 되었고 구체화 되었다. 가장 좋았던 점은 추상이라는 개념에 대해서 먼저 이해하고 그를 바탕으로 구체적인 원칙들을 학습해나가니 그것들이 왜 필요한지를 자연스럽게 알며 학습할 수 있었다는 점이다. 평소에도 나름 잘 지켜온 원칙들도 있었는데 왜 좋은지에 대한 이유를 명확히 인지하고 강사님과 코드를 통해 구체적인 방법들을 적용해보니 학습하며 뿌듯하고 즐거웠다. 미션기존 코드를 리팩토링 해보는 Day4 미션에서 강의에 나온 원칙들을 충분히 고민하고 적용해보지 못한 것 같아 아쉬움이 남아 다시 진행해보려 한다. SOLID 원칙에 대해서 자신기만의 언어로 바꾸어 설명해보는 미션은 각각 원칙에 대해 내가 생각하기에 가장 중요한 부분을 담고 함축적으로 표현할 수 있도록 아래와 같이 작성해보았다. SRP (단일 책임 원칙) 하나의 클래스는 하나의 책임만 가져야 한다. 즉 하나의 기능에 집중해야 한다. OCP (개방-폐쇄 원칙) 새로운 기능을 추가할 수 있도록 열려 있지만, 기존 코드는 수정하지 않도록 설계해야 한다. LSP (리스코프 치환 원칙) 자식 클래스가 부모 클래스의 기능을 깨뜨리지 않고 확장될 수 있어야 한다. ISP (인터페이스 분리 원칙) 자신이 사용하지 않는 인터페이스에 의존하지 않아야 한다. 하나의 큰 인터페이스보다, 구체적이고 작은 인터페이스 여러 개로 나누는 것이 좋다. DIP (의존 역전 원칙) 고수준 모듈은 저수준 모듈에 의존해서는 안 되며, 둘 다 추상화에 의존해야 한다. 즉, 구체적인 구현이 아니라, 추상화된 인터페이스에 의존해야 한다. 사실 이번주 후반에 여러 스케줄이 겹쳐 학습과정에 충분히 원하는 만큼 시간을 쓰지 못했는데, 주 초반에 미리 미리 시간이 있을 때 해둘 걸 하는 후회를 했다.역시나 시간이 있을 때 해놓아야 미래가 행복하다는 것을 느꼈다. 다음 주는 이런 아쉬움을 동기삼아 더 집중해보아야겠다.