블로그

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

강의를 학습하며 새로 배운 점2주차 학습 범위 :따라하며 배우는 자바스크립트 A-Z 8~10섹션, 따라하며 배우는 React A-Z 2~6섹션이번주는 리액트가 진도에 포함됐다.완전히 처음하는 것은 아니지만 기존에 그냥 리액트가 뭔지 맛보기 수준으로 해보았기에많이 복잡하고 어려웠다. 강의 내용자체는 들으면서 이해는 되지만 막상 내가 프로젝트를 만드려고 하면 문제가 생겼다.특히 tailwindcss를 사용하려고 할 때는 설치하고 지우고 설치하고 지우고를 몇번 반복했는데도 동작하지 않았다...경로도 바꿔보고 혹시 터미널 문제인가 싶어서 ubuntu도 설치해보고 했는데 소용없었다 ㅠㅠ(이전엔 맥북으로 해서 zsh를 썼을때는 프레임워크나 등등을 설치할때 문제가 된 적이 거의 없었고, 윈도우에서 bash를 사용하니 npx 명령어 부터 작동하지 않아서 혹시 싶었다..)리액트를 배우면서 과제까지 해보려고 하니 너무 시간도 부족하고 어려워서 이번주는 배우는 것에만 집중하려고 했다그래서 다음주에 심기일전하고 다시 과제를 해보려고 한다.과제 진행 회고2주차 과제비밀번호 생성 앱 만들기타이핑 테스트 앱 만들기 디즈니 플러스 앱 만들기포켓몬 도감 앱 만들기이번주는 리액트 과제를 포기했다... 다음주에 열심히 해보려고 한다. 비밀번호 생성 앱 만들기가장 여유있을때 한 과제인 만큼 최대한 배운걸 활용해보려고 했다. 객체에 선택지들에 맞는 글자를 넣어두고 ex) 숫자 = "0123456789"랜덤으로 선택된 선택지를 선정 (filter 사용), 선택지 안에 있는 문자열을 인덱스로 접근해서 랜덤하게 하나를 가져왔다그리고 입력한 비밀번호 길이에 맞게 반복해서 비밀번호를 만들었다.filter를 깊게 공부하면서 동적 접근이라는 것도 배웠다 처음엔 이해가 안됐지만 (사실 지금도)사용해보니 훨씬 코드를 단축할 수 있었다.  타이핑 테스트 앱 만들기랜덤 영어 명언 api를 불러 와서 진행했다. (https://api.adviceslip.com/advice)어렸을때 한컴 타자연습을 많이, 그리고 재미있게 했어서 앱에 어떤 기능을 넣어야 하는지는 명확했는데타속이라는 걸 어떤 기준으로 해야하는지는 어려워서 다른 사람들이 한 것들을 많이 검색해보며 참고했다 

rjf1138

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

강의 수강 내용 핵심 정리1. 클린 코드와 추상화클린 코드의 핵심은 가독성 → 코드가 잘 읽히면 이해하기 쉽고 유지보수가 용이함.추상화(Abstract): 구체적인 정보에서 핵심 개념만 뽑아 단순화하는 과정.적절한 추상화는 복잡한 로직과 데이터를 단순화하여 읽기 좋은 코드를 만든다.잘못된 추상화는 문맥이 맞지 않는 용어 사용이나 과도한 단순화로 혼란을 유발할 수 있음.추상화 레벨을 맞추는 것이 중요 → 동일한 코드 블록 내에서는 비슷한 추상화 수준 유지.2. 이름 짓기(Naming)의도를 명확히 전달하는 이름을 사용 (e.g. input, input2 → cellInput, userActionInput).줄임말은 가독성을 해칠 수 있음 → 되도록 풀네임 사용 (e.g. cnt 대신 count).일관된 도메인 용어 사용 → 내부 팀에서 정한 용어를 지키기.단수/복수 명확히 구분 (e.g. row vs rows).getter/setter 사용 최소화 → 객체에 메시지를 보내도록 유도 (isAgeGreaterThan(19)).3. 메서드 설계와 선언부메서드는 하나의 책임(기능)만 가지도록 설계 → 한 가지 이상의 일을 하면 분리.메서드명은 추상적 의미를 담아야 함 (예: processPayment 대신 deductBalance).파라미터와 반환 타입을 신중하게 결정 → void 대신 반환값을 활용하여 테스트 가능하도록 함.메서드의 Depth(중첩 수준) 최소화 → 지나치게 깊은 if문, for문은 리팩토링 필요.4. 가독성을 위한 코드 스타일Early Return 사용 → 불필요한 else 제거로 가독성 향상.공백 라인 활용 → 의미 있는 단위로 코드 분리.부정문 지양 → !isLeft() 대신 isRight() 또는 isNotLeft().매직 넘버 & 매직 스트링 제거 → 10 대신 BOARD_SIZE 사용.변수는 사용되는 곳 가까이 선언 → 코드 이해도를 높이고 유지보수 용이.5. 객체지향 설계 원칙 (SOLID)(1) 단일 책임 원칙 (SRP)한 클래스는 하나의 책임(변경 이유)만 가져야 함.예) 게임 로직과 사용자 입력 처리를 분리 (e.g. GameLogic, UserInputHandler).(2) 개방-폐쇄 원칙 (OCP)기존 코드 수정 없이 기능 확장이 가능해야 함.추상화(인터페이스) 사용으로 변경 사항이 최소화되도록 설계.(3) 리스코프 치환 원칙 (LSP)부모 클래스를 자식 클래스로 대체해도 문제가 없어야 함.자식 클래스에서 부모 클래스의 기능을 임의로 변경하거나 지원하지 않는 기능을 추가하면 LSP 위반.(4) 인터페이스 분리 원칙 (ISP)하나의 인터페이스가 너무 많은 기능을 포함하면 안 됨 → 기능 단위로 분리.예) GameInitializable, GameRunnable로 인터페이스 분리.(5) 의존성 역전 원칙 (DIP)상위 모듈(비즈니스 로직)은 하위 모듈(구현)과 직접적으로 의존하지 않아야 함.인터페이스를 통해 의존성을 주입(DI) → 런타임에 유연한 변경 가능.스프링의 IoC 컨테이너를 활용하면 DIP 자동 적용 가능.6. 예외 처리와 NULL 다루기예외를 의도적으로 구분 → 사용자 예외(AppException) vs 시스템 예외.NULL 사용 최소화 → Optional<T> 활용, orElseGet()으로 성능 최적화.예외 발생 가능성이 높은 구간은 외부 세계와의 접점 (e.g. 입력값, API 응답).7. 코드 리팩토링 접근법처음부터 SOLID 원칙을 완벽하게 적용하려 하지 말고, 도메인 이해를 우선.코드를 작성하면서 점진적으로 리팩토링 → 적절한 추상화와 역할 분리를 고민하며 개선.성능, 유지보수성을 고려하여 때로는 원칙을 트레이드오프할 수도 있음.8. 상속보다는 조합을 사용하자상속은 부모-자식 간 결합도가 높아 수정이 어렵고 유연성이 떨어짐.부모 클래스 변경 시 모든 자식 클래스에 영향을 미침.조합(Composition)과 인터페이스를 활용하면 유연한 구조를 만들 수 있음.코드 중복 제거보다 유연한 설계가 더 중요하다.9. Value Object(VO)와 EntityVO(Value Object): 도메인의 개념을 표현하는 값 객체로, 불변성을 가지며 식별자가 없음.불변성: final 필드 사용, setter 금지.동등성: 값이 같으면 동일한 객체로 취급 → equals() & hashCode() 재정의 필요.유효성 검증: 객체 생성 시점에서 검증 수행.Entity: 식별자가 존재하며, 같은 ID를 가지면 같은 객체로 취급.VO와 Entity의 차이:Entity: 시간이 지나면서 값이 변할 수 있음.VO: 생성된 이후 값이 변하지 않으며, 모든 값이 같아야 같은 객체로 취급됨.→ equals() & hashCode()를 재정의해야 하는 이유와, hash 자료형을 구현하는 방법을 학습할 필요가 있음.10. 일급 컬렉션(First-Class Collection)컬렉션을 단순히 사용하지 않고 객체로 포장하여 의미를 부여함.컬렉션을 감싸면서 로직을 함께 관리 → 가공 로직을 포함하여 유지보수성을 높임.새로운 컬렉션을 반환해야 하는 경우기존 컬렉션을 변경할 여지를 없애기 위해 새로운 리스트(ArrayList 등)를 반환해야 할 때가 있음.예: new ArrayList<>(originalList)는 리스트 객체를 새로 만들지만 내부 요소는 기존 객체를 참조하므로, 원본 데이터를 안전하게 유지하려면 내부 객체도 깊은 복사(Deep Copy)해야 함.11. Enum의 특성과 활용Enum은 단순한 상수가 아닌, 상태와 관련된 로직을 포함할 수 있는 객체.특정 도메인의 개념을 명확하게 표현할 수 있음.변경이 잦은 개념이라면 Enum 대신 DB에서 관리하는 것이 유리함.12. 다형성(Polymorphism) 활용하기반복적인 if-else 문을 줄이기 위해 다형성을 적극 활용.변하는 것은 조건과 행위 → enum + 인터페이스 조합으로 해결 가능.Enum value별로 인터페이스를 구현할 수 있음.한 번 배운 개념도 반복해서 복습하는 것이 중요하다.13. 숨겨진 도메인 개념 도출하기객체 지향은 현실을 100% 반영하는 것이 아니라, 현실을 흉내 내는 것.완벽한 설계는 불가능하며, 그 당시의 최선의 선택이 중요함.시간이 지나면서 틀렸음을 인지할 수도 있기 때문에, 미래 변경 가능성을 고려한 코드 작성이 필요함.미션 해결 과정 정리Day 2 미션우리는 매일 숨을 쉬고 음식을 소화하지만, 이를 매번 세밀하게 설명한다면 너무 복잡하고 불편할 것입니다.이 미션을 수행하면서, 최근 읽고 있는 《데이터 중심 애플리케이션 설계》(a.k.a DDIA)에서 본 문장이 떠올랐습니다."우발적 복잡도(accidental complexity)를 제거하기 위한 최상의 도구는 추상화다."여기서도 추상화라는 개념이 강조됩니다. 또한, 책의 예시에서도 “기계어, CPU 레지스터, 시스템 호출을 추상화한 것이 고수준 프로그래밍 언어이다”라는 설명이 나옵니다. 이는 강의에서 들었던 내용과도 동일합니다.컴퓨터가 실제로 실행하는 것은 어셈블리어와 같은 저수준 코드이지만, 개발자인 우리가 이를 직접 다루는 것은 어렵고 비효율적입니다. 따라서, 우리는 Java 같은 고수준 프로그래밍 언어를 사용하며 자연스럽게 추상화의 이점을 누리고 있는 것입니다.또 다른 추상화의 예시로 떠오른 것은 Spring에서 Database(ex: MySQL)를 사용하기 위한 ORM인 JPA, Spring Data JPA입니다.우리는 데이터베이스를 사용하기 위해 보통 다음과 같은 작업을 수행해야 합니다.Connection Pool에서 Connection을 가져옴데이터베이스와 연결을 맺음SQL 쿼리를 작성하고 실행결과를 받아서 처리이 과정은 데이터베이스를 사용할 때마다 반복되며, 이는 우발적 복잡도를 증가시킵니다. 이를 해결하기 위해 JPA와 Spring Data JPA 같은 ORM이 등장했습니다.즉, 선배 개발자들이 데이터베이스 작업의 복잡성을 추상화하여 보다 간결하고 일관된 방식으로 데이터를 다룰 수 있도록 한 것입니다.또한, Java의 interface 역시 추상화의 대표적인 사례라고 생각합니다.interface는 구현 세부 사항을 숨기고, 다양한 구현체를 유연하게 사용할 수 있도록 하며, 다형성을 활용하여 코드의 재사용성과 유지보수성을 높이는 역할을 합니다.이렇듯, 이번 미션에서는 다시 한번 올바른 추상화의 중요성과 그것이 Readable Code 를 만드는데 얼마나 큰 요소를 차지하는지 잘 알 수 있었습니다.애플리케이션 설계, 코드 구현 등 여러 곳에서 추상이라는 개념이 얼마나 중요하게 여기는지 다시 한번 알 수 있었고, 적재적소에 잘 써먹어야겠다는 생각을 많이 했습니다.Day 4 미션AS-ISpublic boolean validateOrder(Order order) { if (order.getItems().size() == 0) { log.info("주문 항목이 없습니다."); return false; } else { if (order.getTotalPrice() > 0) { if (!order.hasCustomerInfo()) { log.info("사용자 정보가 없습니다."); return false; } else { return true; } } else if (!(order.getTotalPrice() > 0)) { log.info("올바르지 않은 총 가격입니다."); return false; } } return true; } Readable 하지 못한 점 파악if-else 가 중첩되어 있다. → 사고의 depth 를 줄이자!early return 을 하자!부정 연산자(!)의 가독성이 떨어진다.getter 의 연속 사용 → 메서드화 하자!TO-BE@Service @RequiredArgsConstructor public class OrderService { ... public boolean validateOrder(Order order) { if (order.hasNoItems()) { throw new OrderException("주문 항목이 없습니다."); } if (order.hasNotCustomerInfo()) { throw new OrderException("사용자 정보가 없습니다."); } if (order.isInValidTotalPrice())) { throw new OrderException("올바르지 않은 총 가격입니다."); } return true; } } @Entity @Table(name = "orders") @Where(clause = "deleted_at IS NULL") @SQLDelete(sql = "UPDATE orders SET deleted_at = NOW() WHERE id = ?") public class Order { ... private boolean hasNoItems() { return this.items.isEmpty(); } private boolean hasNotCustomerInfo() { return this.customInfo.isEmpty(); } private boolean isTotalPriceLessThanZero() { return this.items.stream().mapToInt(Item::getPrice).sum() <= 0; } } public class OrderException extends RuntimeException { public OrderException(String message) { super(message); } } if-else 중첩문을 if 문 3개로 변환했다.order.getItems().size() == 0 getter 의 연속적 사용을 메서드화하여 Order 객체 안으로 숨겨서 캡슐화했다.!order.hasCustomerInfo() 과 같은 부정 연산자를 없애고, hasNotCustomerInfo 처럼 부정어도 메서드화 하여 가독성을 높였다.추가적으로 if 문 조건에 들어가는 메서드들을 Order class 안으로 옮기고,단순히 log 처리만 하는 것을 넘어서 RuntimeException 인 OrderException 으로 예외처리를 하였다.SOLID에 대하여 자기만의 언어로 정리해 봅시다.SRP : 단일 책임 원칙(single responsibility principle)OCP : 개방-폐쇄 원칙 (Open/closed principle)LSP : 리스코프 치환 원칙 (Liskov substitution principle)ISP : 인터페이스 분리 원칙 (Interface segregation principle)DIP : 의존관계 역전 원칙 (Dependency inversion principle)SRP(단일 책임 원칙)한 클래스는 하나의 책임만 가져야 한다.중요한 기준은 변경이다. 변경이 있을 때 파급 효과가 적으면 단일 책임 원칙을 잘 따른 것OCP(개방-폐쇄 원칙)소프트웨어 요소는 확장에 열려 있으나 변경에는 닫혀 있어야 한다.다형성을 활용해보자인터페이스를 구현한 새로운 클래스를 하나 만들어서 새로운 기능을 구현하면 코드에 변경이 없다.LSP(리스코프 치환 원칙)프로그램의 객체는 프로그램의 정확성을 깨뜨리지 않으면서 하위 타입의 인스턴스로 바꿀 수 있어야 한다.다형성에서 하위 클래스는 인터페이스 규약을 다 지켜야 한다는 것ISP(인터페이스 분리 원칙)특정 클라이언트를 위한 인터페이스 여러 개가 범용 인터페이스 하나보다 낫다덩어리가 크면 그걸 다 구현하기가 힘들다. 덩어리가 작으면 작은 기능만 구현하면 되니까 훨씬 쉬워진다.인터페이스가 명확해지고, 대체 가능성이 높아진다.DIP(의존관계 역전 원칙)프로그래머는 추상화(역할)에 의존해야지, 구체화(구현)에 의존하면 안된다. 의존성 주입은 이 원칙을 따르는 방법 중 하나다.쉽게 이야기해서 구현 클래스에 의존하지 말고, 인터페이스에 의존하라는 뜻앞에서 이야기한 역할(Role)에 의존하게 해야 한다는 것과 같다.객체 세상도 클라이언트가 인터페이스에 의존해야 유연하게 구현체를 변경할 수 있다. 구현체에 의존하게 되면 변경이 아주 어려워진다.개인적으로 Spring Bible 로 생각하는 토비의 스프링, 김영한님의 Spring 강의를 듣고 채득한 내용을 바탕으로 SOLID 개념을 정리해봤습니다.일주일 회고다른 워밍업 클럽과는 달리 한 달 안에 2개의 강의, 그것도 강의당 대략 13시간 정도의 강의를 수강해야 해서 강의를 빨리 들어야 하는 압박감이 들어서 개인적으로는 쉽지 않았던 것 같습니다.그래도, 진도에 늦지 않게 강의를 수강한 것 같아서 무척 뿌듯하네요.그리고, 이번주에는 미션이 2개 있었는데 해당 미션을 수행하면서 강사님이 중요하게 생각하는 건 무엇인지, 강의를 들을 때 어떤 걸 중점적으로 들어야 하는지에 대한 일종의 가이드라인을 주시는 것 같아서 강의를 수강하기에 좀 더 수월 했던 것 같습니다.현재, 2년 2개월을 회사 생활을 마치고, 이직 준비를 한지 벌써 1년이 지나면서 바쁘게 취직 준비를 하고 있지만 그럼에도 박우빈님의 강의를 들으면서 클린코드/테스트코드를 공부하기 정말 잘했다는 소위 말하는 돈값을 한다는 강의를 느꼈습니다.남은 3주도 열심히 하여 완강은 물론 우수러너도 되어보도록 하겠습니다.

rjf1138

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

💡 강의 핵심 내용 정리📝Readable Code1. 주석의 역할과 사용 원칙🚫 주석을 최소화해야 하는 이유주석이 많다는 것은 비즈니스 요구사항을 코드로 잘 표현하지 못한 것을 의미한다.주석에 의존하면 적절한 추상화 수준을 유지하기 어려워 코드 품질이 낮아진다.✅ 언제 주석을 사용해야 할까?코드로 표현할 수 없는 의사 결정의 히스토리를 남길 때.자주 변경되지 않는 정보만 포함하고, 지속적으로 관리해야 한다.부정확한 주석은 차라리 없는 것보다 나쁘다.2. 변수와 메서드 정렬 순서🔍 변수 정렬 원칙변수는 사용하는 순서대로 배치하여 가독성을 높인다.📌 메서드 정렬 원칙공개 메서드 (public) 먼저 배치상태 변경 > 판별 > 조회 순으로 배치.같은 기능을 하는 메서드는 그룹화하여 정렬.**비공개 메서드 (private)**는 공개 메서드에서 호출하는 순서대로 정렬.3. 코드 리팩토링 & 가독성 향상🏗 주석 없이 코드로 의미 전달하기 (Enum 활용)java복사편집// ❌ 기존 코드 private int gameStatus = 0; // 0: 게임 중, 1: 승리, -1: 패배 // ✅ 개선 코드 public enum GameStatus { IN_PROGRESS("진행 중"), WIN("승리"), LOSE("패배"); }🎯 객체 역할 분리GameBoard → 게임 진행 및 로직 담당 (비즈니스 로직)MineSweeper → 사용자 입력/출력 제어 (컨트롤러 역할)4. 패키지 구조 개선📌 패키지는 "문맥"을 제공한다유지보수를 고려하여 적절한 수준으로 패키지를 나눈다.패키지 변경은 팀원과 협의 후 진행하는 것이 좋다.5. IDE 도구 활용⚙ 코드 자동 정렬Option + Command + L (Mac, IntelliJ)🔍 코드 품질 유지 도구SonarCube, lint (ESLint, ktlint).editorconfig를 활용해 일관된 코드 스타일 유지6. 오버 엔지니어링 방지🚫 불필요한 추상화 & 인터페이스 남발구현체가 하나뿐인 인터페이스는 불필요한 경우가 많다.추상화를 도입하면 정보가 숨겨지므로 복잡성이 증가.필요 이상으로 정보를 숨기면 코드 유지보수가 어려워진다.7. 실무에서 코드 품질 vs 빠른 개발⚖ 균형 잡힌 선택이 필요하다완벽한 코드보다 실용적인 코드가 중요하다.클린 코드를 목표로 하되, 현실적인 개발 속도도 고려해야 한다.TODO 주석 등을 활용하여 미래 리팩토링을 위한 가이드 남기기.8. 결론: "완벽한 코드"는 없다클린 코드도 은탄환이 아니다. 적절한 수준에서 적용하는 것이 중요하다.테스트 코드도 잘 관리하지 않으면 오히려 유지보수 부담이 될 수 있다.가장 중요한 것은 도메인 지식을 늘리고, 코드가 어떤 비즈니스 요구사항을 해결하는지 파악하는 것 💻Practical Testing1. 테스트 코드의 필요성🤔 왜 테스트 코드가 중요한가?자동화된 테스트를 통해 빠르고 정확한 피드백 제공.사람이 직접 테스트하면 비효율적이고 실수가 발생할 가능성이 높음.🚫 수동 테스트의 문제점java복사편집@Test void add_manual_test() { CafeKiosk cafeKiosk = new CafeKiosk(); cafeKiosk.add(new Americano()); System.out.println(">>> 담긴 음료 수 : " + cafeKiosk.getBeverages().size()); } ❌ 사람이 직접 확인해야 하므로 자동화되지 않고 오류가 발생할 가능성이 높음.✅ JUnit5 & AssertJ 활용한 자동화 테스트java복사편집@Test void add() { CafeKiosk cafeKiosk = new CafeKiosk(); cafeKiosk.add(new Americano()); assertThat(cafeKiosk.getBeverages()).hasSize(1); assertThat(cafeKiosk.getBeverages().get(0).getName()).isEqualTo("아메리카노"); } ✔ 자동화된 검증을 통해 일관성 유지✔ 테스트 실패 시 바로 원인 파악 가능2. 테스트 케이스 설계🔍 경계값 테스트범위 (이상, 이하, 초과, 미만), 구간, 날짜 등 고려하여 테스트 설계. 💡 미션 Day 7이번주 미션은 StudyCafe 라는 프로젝트(스터디 카페 이용권 구매 서비스)를 리팩토링하는 미션이었다.하지만, 시간이 부족하여 내 자신이 만족할 만한 코드를 작성하지 못하였고, 계속 수정하다가 결국 마감 시간 보다도 늦게 제출하고 말았다...이후에, 중간점검 날 다른 분들의 피드백도 보고 깨달은 바가 많았다. 꼭 다시 새로운 마음으로 리팩토링 하여 마감 기한에 상관 없이 다시 제출해보고, 지금 작성 중인 2주차 발자국에도 내가 어떤 과정 및 생각으로 미션을 수행했는지 업데이트 할 것이다.💡 한주 회고이번 한주도 엄청 바빴다. 읽기 좋은 코드 강의를 마무리하고 새로 테스트 코드 강의로 들어섰다. 이전 회사에서 테스트 코드를 제대로 짜본 적이 없고 또 그럴 환경이 뒷바쳐 주지 못해서 작성하지 못하였다. 그래서, 테스트 코드에 대한 갈증이 더욱 컸다. 이번 기회에 제대로 배우고 시야를 넓혀가야겠다. Day 11 미션에는 꼭 강사님께 코드 리뷰 제출까지 해봐야겠다. 화이팅!!

주이

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

FIFO 스케줄링의 장단점이 뭔가요? 장점단순하고 직관적단점한 프로세스가 완전히 끝나야 다음 프로세스가 진행되므로, 실행시간이 짧고 늦게 도착한 프로세스가 실행시간이 길고 빨리 도착한 프로세스의 작업을 기다려야 함I/O 작업이 있다면, CPU는 I/O 작업이 끝날 때까지 쉬고있기 때문에 CPU 사용률이 떨어짐 2. SJF를 사용하기 어려운 이유가 뭔가요?어떤 프로세스가 얼마나 실행될지 예측하기 어려움Burst Time이 긴 프로세스는 아주 오래동안 실행되지 않을 수 있음-> SJF는 Burst Time이 짧은 프로세스가 먼저 실행되기 때문에 긴 프로세스는 뒤로 밀려남  3. RR 스케줄링에서 타임 슬라이스가 아주 작으면 어떤 문제가 발생할까요?오버헤드가 너무 커진다. 컨텍스트 스위칭이 자주 일어나게 되고, 타임 슬라이스에서 실행되는 처리량보다 컨텍스트 스위칭을 처리하는 양이 더 많아지기 때문 운영체제가 MLFQ에서 CPU Bound Process와 I/O Bound Process를 어떻게 구분할까요?CPU를 사용하는 프로세스가 실행하다가 스스로 CPU를 반납하면 CPU 사용이 적음 -> I/O Bound Process 확률 증가타임 슬라이스 크기를 오버해서 CPU 스케줄러에 의해 강제로 CPU를 뺏기는 상황 -> CPU Bound Process 확률 증가 공유자원이란 무엇인가요?프로세스 간 통신을 할 때 공동으로 이용하는 변수나 파일 교착상태에 빠질 수 있는 조건은 어떤 것들을 충족해야 할까요?상호 배제: 어떤 프로세스가 한 리소스를 점유했다면, 그 리소스는 다른 프로세스에게 공유되면 안 된다.비선점: 다른 프로세스가 리소스를 뺏을 수 없어야 한다.점유와 대기: 어떤 리소스를 가지고 있을 때, 다른 리소스를 원하는 상태여야 한다. 원형 대기: 점유와 대기를 하는 프로세스들의 관계가 원형을 이룬다. (원하는 리소스의 방향이 원형을 이룸) 

huihui

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

강의 수강Readable Code: 읽기 좋은 코드를 작성하는 사고법 학습 내용 요약섹션2. 추상추상화: 구체에서 정보를 제거하고 함축하여 중요한 것만 남기는 과정구체화: 추상을 보고 유추를 통해 생략된 정보를 재현해내서 이해하는 과정추상화 레벨: 얼마나 추상화했는지를 나타내는 단계고수준: 추상화 레벨이 높다 (추상적)저수준: 추상화 레벨이 낮다 (구체적)적절한 추상화: 해당 도메인의 문맥 안에서 핵심 개념만 남겨서 표현하는 것-> 적절한 추상화는 복잡한 데이터/로직을 단순화하여 코드를 이해하기 쉽게 한다.이름 짓기, 메서드로 추출 + 메서드 선언부, 매직 넘버/매직 스트링 상수로 추출섹션3. 논리, 사고의 흐름뇌 메모리 적게 쓰기 (인지적 경제성)코드를 작성할 때 읽는 사람의 뇌 메모리를 최대한 적게 사용하도록 작성할 것Early returnearly return: 메서드를 분리해 끝낼 수 있는 케이스들은 빨리 return 해버려 아래쪽 케이스를 읽을 때 위쪽 케이스를 신경쓰지 않아도 되도록 하는 것 사고의 depth 줄이기코드를 읽는 사람의 사고가 적당한 수준으로(추상화된 정도로) 이해할 수 있도록 메서드 분리공백 라인으로 의미 단위 표현부정 연산자 제거예외 처리개발자가 의도한 예외개발자가 의도하지 않은 예외섹션4. 객체 지향 패러다임 관심사의 분리: 관심사에 따라 기능과 책임을 나누어 객체 생성높은 응집도낮은 결합도SOLIDSRP(단일 책임 원칙)하나의 클래스는 하나의 책임(관심사)만을 가져야 한다.SRP를 지킴으로써 각 클래스의 응집도를 높이고, 클래스 간의 결합도를 낮출 수 있다.OCP(개방-폐쇄 원칙)확장에는 열려 있고, 변경에는 닫혀 있어야 한다.기존 코드의 변경 없이도 시스템의 기능을 확장할 수 있어야 한다.추상화와 다형성을 활용해서 OCP를 지킬 수 있다.LSP(리스코프 치환 원칙)부모 클래스의 인스턴스를 자식 클래스의 인스턴스로 치환해도 정상 작동해야 한다.상속 구조에서 자식 클래스는 부모 클래스의 책임을 준수하고 행동을 변경하지 않아야 한다.ISP(인터페이스 분리 원칙)클라이언트는 자신이 사용하지 않는 인터페이스에 의존하면 안된다.인터페이스를 필요한 기능 단위로 잘게 쪼개서 사용해야 한다.DIP(의존성 역전 원칙)상위 수준의 모듈은 하위 수준의 모듈에 의존해서는 안 된다.둘 모두 추상화에 의존해야 한다.구체적인 구현 클래스가 아닌 추상화(인터페이스, 추상 클래스)에 의존해야 한다.섹션5. 객체 지향 적용하기  회고평소에 코드를 짤 때 좋은 코드 작성에 대해서 깊게 생각하지 않았다. 일단 코드가 제기능을 하는지가 가장 큰 관심사였고, 개인 프로젝트를 주로 해왔기 때문에 내가 이해할 수 있으면 가독성에 대해 크게 신경쓰지 않았기 때문이다. 그래서 읽는 사람보다는 코드를 작성하는 나의 입장에서 편한 방향으로 코드를 작성해왔다.강의를 왜 좋은 코드를 작성해야 하는지 명확하게 이해하고, 지금껏 생각하지 못한 관점을 배울 수 있었다. 또한 강사님을 따라 코드를 리팩토링하며 앞으로 코드를 짤 때 배운 내용을 적용하여 읽는 이가 이해하기 쉬운 코드를 짤 수 있도록 노력해야 겠다는 다짐을 하게 되었다.칭찬하고 싶은점: 바쁜 한주였지만 매일 학습 진도표에 맞춰서 미루지 않고 강의를 들었다.아쉬웠던 점: 강의 내용을 정리하며 학습하는 것은 조금 밀렸고, 미션 수행만으로는 학습한 내용을 내가 온전히 이해하고 있는지 확인하기 어려웠다.보완할 점: 이론적인 내용을 정리하는 것 외에도 배운 내용을 실제 코드에 적용해보는 연습이 더 필요할 것 같다.다음 주 목표: 강의와 미션 외에도 기존에 내가 짠 코드를 리팩토링 해봐야겠다. 미션코드 리팩토링리팩토링 전public boolean validateOrder(Order order) { if (order.getItems().size() == 0) { log.info("주문 항목이 없습니다."); return false; } else { if (order.getTotalPrice() > 0) { if (!order.hasCustomerInfo()) { log.info("사용자 정보가 없습니다."); return false; } else { return true; } } else if (!(order.getTotalPrice() > 0)) { log.info("올바르지 않은 총 가격입니다."); return false; } } return true; } 리팩토링 후 public static final String EMPTY_ORDER_MESSAGE = "주문 항목이 없습니다."; public static final String INVALID_TOTAL_PRICE_MESSAGE = "올바르지 않은 총 가격입니다."; public static final String NO_USER_INFO_MESSAGE = "사용자 정보가 없습니다."; public boolean validateOrder(Order order) { if (order.hasNoItem()) { log.info(EMPTY_ORDER_MESSAGE); return false; } if (order.hasInvalidTotalPrice()) { log.info(INVALID_TOTAL_PRICE_MESSAGE); return false; } if (order.hasNoCustomerInfo()) { log.info(NO_USER_INFO_MESSAGE); return false; } return true; }미션 해결 과정코드를 살펴보며 강의에서 배운 내용을 하나씩 적용해보았다.중첩되어 복잡한 분기문 -> Early return을 적용해 불필요한 else와 중첩된 분기 제거if문의 조건이 복잡함 -> if문의 조건을 메서드로 추출 분기 조건이 한번에 이해되도록 함 (사고의 depth 줄임)또한 order의 정보를 객체 외부에서 getter로 빼와 조작하는 대신 order가 처리하게 하여 캡슐화를if문 조건에 부정 연산자 존재 -> 별도의 메서드(hasNoItem())를 만들어 if문 조건의 부정 연산자(!) 제거매직 스트링이었던 로그 메시지를 상수로 추출해 가독성을 높이고 유지보수를 용이하게 함읽는 이가 의미 단위로 이해할 수 있도록 공백 라인 추가회고if문의 조건을 order의 메서드로 추출하는 과정에서 메서드의 이름을 짓는 것이 생각보다 고민되었다. 좀 더 많은 코드를 보며 이런 기능의 메서드의 이름을 관용적으로 어떻게 짓는지 공부해볼 필요가 있어 보인다.다시 보니 log.info()로 처리되고 있는 예외 처리 부분을 thrwo와 try-catch로 관리하는 것이 유지보수에 좋았을 것 같다는 생각이 든다.

백엔드

huihui

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

강의 수강Readable Code: 읽기 좋은 코드를 작성하는 사고법 학습 내용 요약섹션6. 코드 다듬기좋은 주석주석의 양면성주석은 코드로 표현할 수 있는 내용을 대체하는 것으로, 지양해야 함의사 결정의 히스토리를 코드로 표현할 수 없을 때 주석으로 설명해야 함으로 필수임좋은 주석: 코드를 통해 최대한 표현했음에도 전달하지 못한 정보가 남았을 때 사용하는 주석나열 순서변수: 사용하는 순서대로 나열 (인지적 경제성 고려)객체의 공개/비공개 메서드공개 메서드는 기준(중요도, 종류 등)을 가지고 배치비공개 메서드는 공개 메서드에 언급된 순서대로 나열나열 순서로도 의도와 정보 전달 가능 패키지 나누기패키지는 문맥으로써의 정보를 제공패키지 분리 시 유의할 점적절히 쪼개야 함 (너무 안 쪼개면 관리 어렵고, 너무 쪼개도 유지보수 어려움)공통으로 사용하는 클래스들의 패키지 분리/변경은 충돌을 야기함패키지 구조는 가능한 한 초기 프로젝트 설정 때 잘 고민해서 나누기기능 유지보수하기버그 잡기알고리즘 교체IDE 도움 받기 정렬 단축키: Ctrl + Alt + Llinting, style - sonarlint, editorconfig섹션7. 리팩토링 연습메서드 추출로 추상화 레벨 맞추기Optional객체에 메시지 보내기객체의 책임과 응집도IO 통합일급컬렉션display()Order 추출추상화 관점의 차이구현에 초점을 맞춘 추상화 vs. 도메인 개념에 초점을 맞춘 추상화섹션8. 기억하면 좋은 조언들 능동적 읽기복잡하고 엉망인 코드를 읽고 이해해야 할 때 리팩토링하면서 읽기공백으로 단락 구분메서드와 객체로 추상화이해한 내용 주석으로 표기오버 엔지니어링필요한 적정 수준보다 더 높은 수준의 엔지니어링 (불필요)적절하게 적용해서 리팩토링할 것은탄환은 없다항상 정답인 기술은 없다한계까지 연습해보고, 적정 수준, 적정 시점을 깨닫기섹션3. 단위 테스트단위 테스트작은 코드 단위(클래스 or 메서드)를 독립적으로 검증하는 테스트검증 속도가 빠르고 안정적임수동 테스트, 자동화 테스트수동 테스트: 사람이 검증 (콘솔에 출력)자동화 테스트: 기계가 검증Junit5, AssertJJunit5: 단위 테스트를 위한 테스트 프레임워크AssertJ: 테스트 코드 작성을 돕는 테스트 라이브러리해피 테스트, 예외 케이스경계값 테스트:범위, 구간의 경계값으로 테스트 테스트하기 쉬운/어려운 영역 (순수함수)테스트하기 어려운 영역관측할 때마다 다른 값에 의존하는 코드외부 세계에 영향을 주는 코드 (외부에 의존하는 코드)순수 함수테스트하기 쉬운 코드같은 입력에 항상 같은 결과 출력 lombok@Data, @Setter, @AllArgsConstructor 지양양방향 연관관계 시 @ToString 순환 참조 문제섹션4. TDD: Test Driven DevelopmentTDD (테스트 주도 개발)프로덕션 코드보다 테스트 코드를 먼저 작성 -> 테스트가 구현 과정을 주도하도록 하는 방법론 레드-그린-리팩토링레드: 실패하는 테스트 작성그린: 테스트를 통과하는 최소한의 코딩리팩토링: 구현 코드 개선 및 테스트 통과 유지애자일(Agile) 방법론 vs. 폭포수 방법론익트트림 프로그래밍 (XP, eXtreme Programming) 빠른 개발 주기와 지속적인 피드백을 중심으로 하는 소프트웨어 개발 방법론 스크럼(Scrum), 칸반(kanban)스크럼: 짧은 개발 스프린트를 반복적으로 진행해 빠르게 결과물을 만들고 지속적으로 개선칸반: 지속적인 개선과 작업량 관리를 위해 작업을 시각적으로 표현섹션5. 테스트는 []다.-> 테스트 코드는 문서다프로덕션 기능을 설명하는 테스트 코드 문서다양한 테스트 케이스를 통해 프로덕션 코드를 이해하는 시각과 관점을 보완어느 한 사람이 과거에 경험했던 고민의 결과물을 팀 차원으로 승격시켜 모두의 자산으로 공유@DisplayName - 도메인 정책, 용어를 사용한 명확한 문장메서드명만으로는 테스트의 의도 파악 어려움DisplayName을 섬세하게 (모두가 명확히 알아볼 수 있도록)명사의 나열보다는 문장으로 기술테스트 결과까지 기술도메인 용어를 사용하여 한층 추상화된 내용을 담Given / When / Then - 주어진 환경, 행동, 상태 변화Given: 시나리오 진행에 필요한 모든 준비 과정When: 시나리오 행동 진행Then: 시나리오 진행에 대한 결과 명시, 검증BDDTDD에서 파생된 개발 방법시나리오에 기반한 테스트 케이스 자체에 집중하여 테스트JUnit vs. SpockSpcok: Groovy 언어로 BDD 패턴을 적용해서 테스트 코드를 작성  회고바쁜 한주를 정신없이 보내고 나니 벌써 중간점검을 할 시기라 좀 놀랐다. 이번주는 할일이 많아 강의 진도를 따라가기 급급한 나머지 Day 7 미션은 기한 내에 끝내지 못했다. 강의를 들으며 강사님을 따라할 때는 어렵지 않았는데, 막상 배운 내용을 적용해보려 하니 생각보다 막막하고 오랫동안 고민해야 했다. 아쉽게도 강의를 끝내지 못한 상태로 중간 점검 라이브에 참여했지만, 다른 사람들이 리팩토링한 코드와 그에 대한 강사님의 피드백을 보며 코드를 리팩토링하는 다양한 접근법을 확인할 수 있어서 좋았다. 동시에 내가 얼마나 부족한지 깨닫는 시간이기도 했다... 이번 스터디가 끝나더라도 배운 내용을 다시 한번 정리하고, 코드를 리팩토링하는 것도 꾸준히 연습해야겠다.칭찬하고 싶은점: 바쁜 한주였지만 들어야 하는 강의는 다 들었고, 지난주에 하지 못한 정리도 끝냈다.아쉬웠던 점: 너무 바빠서 이번주 미션은 끝내지 못했다.보완할 점: 다음 주말에 Day 7 미션을 끝내고, 다른 사람들이 리팩토링한 것과 비교하며 공부하기다음 주 목표: 강의와 미션 진도표에 맞춰서 끝내기 

백엔드

이지민

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

자료구조재귀함수에서 기저조건을 만들지 않거나 잘못 설정했을 때 어떤 문제가 발생할 수 있나요?종료 조건을 설정하지 않거나 잘못 설정했을 경우 콜스택의 누적으로 인한 메모리 부족으로 강제 종료되거나 혹은 의도하지 않은 결과값을 얻을 수 있습니다.0부터 입력 n까지 홀수의 합을 더하는 재귀 함수를 만들어보세요.function sumOdd(n){ // 탈출(기저) 조건 : n이 1일 때 if (n == 1){ return 1; } // 재귀 조건 : n이 1보다 큰 홀수일 때 if (n%2 == 1){ return sumOdd(n-2) + n; } else{ return sumOdd(n-1); } } console.log(sumOdd(10)) // 25다음 코드는 매개변수로 주어진 파일 경로(.는 현재 디렉토리)에 있는 하위 모든 파일과 디렉토리를 출력하는 코드입니다. 다음 코드를 재귀 함수를 이용하는 코드로 변경해보세요.변경 이전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("."); // 현재 경로의 모든 하위 경로의 파일, 디렉토리 출력 변경 이후const fs = require("fs"); // 파일을 이용하는 모듈 const path = require("path"); // 폴더와 파일의 경로를 지정해주는 모듈 function traverseDirectory2(directory){ const files = fs.readdirSync(directory); for (const file of files) { // 모든 파일 및 디렉토리를 순회 const filePath = path.join(directory, file); // 현재 디렉토리와 파일/디렉토리 이름을 결합하여 전체 경로 생성 const fileStatus = fs.statSync(filePath); // 파일/디렉토리 정보를 가져옴 /* 재귀조건: 해당 경로가 디렉토리인 경우 */ if (fileStatus.isDirectory()) { console.log('디렉토리:', filePath); traverseDirectoryRecursive(filePath); // 재귀 호출 } /* 탈출조건: 해당 경로가 파일인 경우 */ else { console.log('파일:', filePath); } } } traverseDirectory2("."); // 현재 경로의 모든 하위 경로의 파일, 디렉토리 출력

[2주차 발자국]시작하는 PM/PO들에게 알려주고 싶은, 프로덕트의 모든 것 (김민우님)

이제 실질적으로 PM이 해결, 집중해야할 문제들과 해당 문제들을 어떻게 해결 할 수 있을지의 방법론에 대해서 배웠다. 애플디벨로퍼 아카데미에서 Macro한입독서 어플리케이션, MC2 Numvert 어플리케이션의 사용자 인터뷰(니즈를 찾고 앱을 기획하기 위한)를 진행할때 실수했던 부분, 그리고 교훈을 삼고 레슨런/인사이트를 얻은 부분이 있었는데 오늘 배운 내용이 그러한 부분이어서 반갑기도 하면서 이렇게 명확하게 개념으로 얻어갈 수 있어서 좋았다.  흔히 사용자 인터뷰를 하게되면 '답정너' 방식의 인터뷰를 쓰게 될때가 있는데 이를 주의하고 방지하기 위한 인터뷰 설계가 매우 중요하다. (실제로 MC3 TeamOn 어플리케이션을 기획하면서의 사용자 인터뷰때는 리허설까지 진행할정도로 인터뷰설계를 촘촘히 하려고 노력했던 것처럼, 설계의 형식과 내용 모두 중요하다고 생각한다) 결국 사용자 인터뷰 또한 PM이 정보를 얻는 방식중에 하나이기 때문에 원하는 것을 정확히 얻기위해서 목적을 명시화 하는 것 내가 뭘 모르는 지 아는 것 1번과 2번을 모두 고려한 인터뷰 내용과 방식의 설계 가 중요하다. 이 3번을 어떻게 할지에 대해 구체적인 사례들을 알려주셔서 도움이 많이 되었고, 또 안티패턴도 너무 공감하면서 들었다.  또한 인뎁스(심층, 그리고 가능하면 대면) 인터뷰의 중요성에 대해서도 프로젝트를 하며 많이 느꼈었는데 그 부분도 짚어주셔서 감사했다. 나 역시 구글 설문조사로 리서치를 진행할때도 있었지만 결국 특정 문제는(풀기 어려운 문제가 될수록) 유저를 직접 만나고 면대면, 1대1로 꼬리질문(후속질문)을 통해서 얻을 수 있는 정보들이 훨씬 많다는 걸 느꼈고, 포항에서 서울로 올라와서 당일 인터뷰를 하고 돌아갈 정도로 인터뷰에 진심이 되었다. 그리고 후속질문을 할때는 인터뷰이에게 제한을 너무 두지 말것(혹은 가정에 갇히지말것) 등등 와닿는 내용이 많았다. 실제로 MC2프로젝트 당시 아이스브레이킹, 혹은 좀 더 편안한 무드에서의 자연스러운 대답(이 대답으로부터 인사이트를 얻고 의향을 뽑아내는 건 그 다음 pm 이 알아서 할 일이지 유저에게 떠넘길일이 아니다) 이 필요했던 상황이 있었는데, 질문지는 준비되어 있었지만 조금 더 개방적인 질문을 나는 하려고 시도 했던 적이 있었다. 이때 내가 하는 스타일의 개방적인, 혹은 꼬리질문을 더 많이 하게 하지 못한게 판단 미스였던 것 같다고 동료가 이야기해준 적도 있었다. 이렇듯 다양한 안티패턴들을 사용자의 정보를 얻는 과정에서 pm은 하게 되는 것 같다(나 역시 그러하다) 그렇기 때문에 어떻게 이 안티패턴을 극복해 나갈 수 있는지를 고민해야하고 이번강의는 그러한 내용을 명시화 해주시고 생각할 기회를 제공해주셔서 도움이 많이 되었다.   

PMPO

codestudy

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

[인프런 워밍업 스터디 클럽 3기 PM/PO] 2주차 발자국📚 주간 학습 내용 요약1. 고객을 직접 만나기의 중요성PM은 '고객 전문가'가 되어야 성공적인 제품을 만들 수 있음간접적으로 고객 의견을 전달받는 것(VOC, 영업팀 보고)보다 직접 만남이 필수적고객의 경험, 생각, 감정을 생생하게 이해하고 정확한 멘탈 모델 구축 가능2. 효과적인 고객 리서치 설계 방법명확한 리서치 목적과 해결하려는 문제 정의가 먼저적절한 리서치 방법론(정성적/정량적) 선택 중요타겟 고객 선정과 실행 계획 수립이 성공적인 리서치의 기본3. 심층 인터뷰 기법1대1 심층 인터뷰는 고객을 파악하는 가장 강력한 도구실제 행동, 최근 경험, 구체적 상황을 중심으로 질문 설계비즈니스 질문 → 리서치 질문 → 인터뷰 질문으로 단계적 발전4. 사용성 테스트의 중요성사용자가 제품을 얼마나 쉽게 이해하고 사용할 수 있는지 평가5명 정도의 소수 참가자로도 대부분의 사용성 문제 발견 가능'지식의 저주'를 극복하는 방법: 개발자는 제품을 잘 알지만 사용자는 그렇지 않음5. 데이터 활용의 핵심데이터를 기반으로 한 의사결정 프로세스 구축사용자 행동을 로그와 이벤트로 체계적으로 기록의미 있는 데이터를 수집하고 분석하여 실질적인 인사이트 도출🌟 핵심 학습 인사이트배운 점소수의 심층 인터뷰가 대규모 설문보다 더 가치 있는 경우가 많음제품 개발자는 사용성을 50% 이상 예상하지만, 실제 사용자 정답률은 2.5%에 불과한 경우가 많음 (지식의 저주)사용자 경험을 직접 관찰하고 데이터로 검증하는 이중 접근법의 중요성인터뷰에서 추상적인 질문보다 구체적인 경험을 물어보는 것이 더 정확한 정보를 얻는 방법💭 미션에 대한 회고코촉촉 서비스를 위한 고객 조사 계획을 설계하면서 반려동물 주인들의 실제 니즈와 페인 포인트를 정확히 파악하는 중요성을 깨달았습니다. 신뢰와 안전이 핵심인 반려동물 돌봄 서비스에는 심층 인터뷰와 사용성 테스트가 설문조사보다 효과적임을 인식했습니다.처음에는 기술적 측면에 너무 집중했지만, PM은 기술 스택보다 사용자 니즈와 제품 방향성에 집중해야 함을 배웠습니다. '지식의 저주' 개념을 통해 개발팀이 사용자의 제품 이해도를 과대평가하기 쉽다는 점을 이해했고, 이를 극복하기 위한 체계적인 사용성 테스트의 가치를 깨달았습니다.비즈니스 질문에서 구체적인 인터뷰 질문으로 단계적으로 발전시키는 방법론을 통해 코촉촉의 핵심 가치에 집중한 실용적인 질문들을 도출할 수 있었습니다. 앞으로는 이론적 계획을 실제 실행해보고, 정성적 인사이트와 정량적 데이터를 균형 있게 활용하는 역량을 키우고 싶습니다.

기획 · PM· POPMPO고객리서치

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

자바스크립트[8강]Symbol Type이 타입의 목적은 유니크한 식별자를 만들기 위해서 사용됩니다. Symbol.keyFor()란Symbol.for를 이용해서 전역 심볼을 만들 때(찾을 때)사용하는 description을 얻을 수 있는게 symbol.keyFor()입니다.  Iterator(반복기) & Generator(생성기)iterable : 배열은 반복 가능한 객체이며, 반복이 가능하다는 것을 iterable이라고 부릅니다. for…of를 이용할 수 있거나 [Symbol.iterator]()이 값을 가지면 iterable 한 것입니다.iterator : 반복자는 next()를 호출해서 { value: , done: } 두개의 속성을 가지는 객체를 반환하는 객체입니다.Generator Function는 사용자의 요구에 따라 다른 시간 간격으로 여러 값을 반환할 수 있습니다. 일반 함수 => 단 한 번의 실행으로 함수 끝까지 실행됩니다.제너레이터 함수 => 사용자의 요구에 따라 일시적으로 정지될 수도 있고, 다시 시작될 수도 있습니다.Function* -> 제너레이터 함순느 별표가 붙어야함 [9강]디자인패턴소프트웨어 설계의 주어진 콘텍스트 내에서 일반적으로 발생하는 문제에 대한 일반적이고 재사용 가능한 솔루션입니다.디자인 패턴은 프로그래머가 응용 프로그램이나 시스템을 디자인할 때 일반적인 문제를 해결하는 데 사용할 수 있는 공식화된 모범사례입니다. 장점최고의 솔루션 - 여러번의 수정을 통해 완성되었기에 잘 작동하는것을 알고있어 개발자가 자주 사용함재사용성 - 단일문제에만 존재할 수 없어 여러 문제를 해결하기 위해 특정 상황에서 수정할 수 있는 재사용 가능한 솔루션풍부한 표현련 - 부분적으로 설명할 수 있음향상된 의사 소통 - 문제에 대한 공통 목표를 설정하여 잠재적인 문제와 이러한 문제에 대한 솔루션에 대해 서로 의사 소통하는 데 도움이 됨필요없는 코드 리팩토링 - 종종 다양한 문제에 대한 최적의 솔루션코드베이스 크기 감소 - 공간을 거의 차지하지 않고 몇 줄의 코드로 필요한 솔루션을 구현하여 소중한 공간을 보존Singleton싱글톤 패턴은 클래스의 인스턴스화를 하나의 객체로 제한하는 디자인 패턴입니다. 이는 시스템 전체에서 작업을 조정하는 데 정확히 하나의 객체가 필요한 경우에 유용합니다.고전적으로 싱글톤 패턴은 클래스가 존재하지 않은 경우 클래스의 새 인스턴스를 생성하는 메서드로 클래스를 생성하여 구현할 수 있습니다. 인스턴스가 이미 존재하는 경우 해당 개체에 대한 참조를 반환합니다. Factory 팩토리 패턴을 사용하면 특수 함수인 팩토리 함수를 사용하여 비슷한 객체를 많이 만들 수 있습니다. => 비슷한 객체를 반복적으로 생성해야하는 경우 사용Mediator 중재자 패턴은 객체 그룹에 대한 중앙 권한을 제공합니다. Observer옵저버 패턴은 이벤트 드리븐 시스템을 이용하는 것Module 모듈패턴은 코드를 더 작고 재사용 가능한 조각으로 분할하는 것을 도와줍니다. 리액트-컴포넌트리액트로 만들어진 앱을 이루는 최소한의 단위1. 클래스형 컴포넌트class App extends Component { render(){ return <h1>안녕하세요.</h1> }}2 . 함수형 컴포넌트 function App(){ return <h1>안녕하세요.<h1>}-브라우저가 그려지는 원리 및 가상돔리액트의 주요 특징 중 하나는 가상돔을 사용 하는 것브라우저가 랜더링 하는 과정을 살펴보자웹페이지 빌드 과정 : 웹 브라우저가 HTML 문서를 읽고, 스타일 입히고 뷰포트에 표시하는 과정DOM tree 생성 -> Rander tree 생성 -> Layout -> 실제 화면에 그리기문제점 : 어떤 인터렉션에 의해 DOM에 변화가 발생하면 그 때 마다 Render tree가 재생성 됨. 즉 모든 요소들의 스타일을 다시 계산(레이아웃, 화면 재그리기) , 불필요한 DOM을 조작하는 비용이 너무 크게 됨 이러한 문제점으로 인해 나온 것이 가상 돔. 가상돔이란 실제 DOM을 메모리에 복사해준 것으로 생각하면 됨작동원리: 데이터가 바뀌면 가상돔에 렌더링되고 이전에 생긴 가상돔과 비교해서 바뀐 부분만 실제 돔에 적용을 시켜줌. 바뀐 부분을 찾는 과정을 Diffing이라고 부르며, 바뀐 부분만 실제 돔에 적용시켜주는 것을 재조정이라고 부름 -react app 실행해보기npm run start : 이 명령어로 리액트 앱을 시작할 수 있음-SPAApp.js파일의 소스 코드를 변경하면 변경한 부분이 화면에 적용이 됨public/index.htmlHTML 템플릿 파일입니다. 하단의 div 엘리먼트의 id를 root로 해놓음<div id=“root”></div> scr/index.js자바스크립트의 시작점. 여기서 위에 root id를 가진 div 엘리먼트를 잡아 줌. 그래서 그 엘리먼트 안에서 화면을 꾸밀 수 있게 됨ReactDOM.render( <React.StrictMode> <App /> </React.StricMode), document.getElementById(‘root’));Single Page Application(SPA)원래 a페이지를 만들면 a.html b페이지를 만들면 b.html 이런식으로 만듦 (전통적인 웹 사이트 만들때 사용하는 멀티 페이지 어플리케이션)하지만 요즘엔 웹 사이트의 전체 페이지를 하나의 페이지에 담아 동적으로 화면을 바꿔가며 표현함 (싱글 페이지 어플리케이션 SPA) 과제비밀번호 생성기 앱객체에 각 조건의 값들을 만들어 체크된 것들의 랜덤으로 비밀번호 생성되며, 구현한 값 클릭 시 복사 가능하도록 구현 하였다  

junghlee234

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

진행 기간: 2주차(20250310-20250316)진행 강의:[UI3 업데이트] 피그마 배리어블을 활용한 디자인 시스템 구축하기강의 및 미션이번 주는 입력과 디스플레이 컴포넌트를 만들어보는 주였습니다.강의 내용 및 미션도 저번 주와는 다르게 구현을 많이 하면서 기본 컴포넌트들을 만들어나가는 부분이 많았습니다.특히 컴포넌트의 속성의 조합에 따른 다양한 파생 베리언트들을 만들어 보는 것이 중요했습니다.미션을 수행하기 위해서는 숙련도 부족으로 정확한 확인을 위해 결국 강의 하나하나를 다시 천천히 들어야 하기에, 특히 시간이 필요하였습니다.회고이번 주는 수술과 좋지 않은 몸 상태로 인해 실제로는 2일(하루 4시간 기준) 이상을 낼 수 없었기에 목표하고자 하는 미션의 절반 이하로만 진행이 가능하였습니다.확인 결과 3주차의 경우에는 컴포넌트의 고도화 및 마무리 과정으로, 거의 하루 1개 미션을 진행해야 하는 상황이므로 다음 주 1일 차에 못한 강의와 미션을 마저 처리하고, 남은 4일에 하루 2개 정도로 처리하고, 만약에 부족한 경우 주말에 처리하는 식으로 진행할 예정입니다.또한 미션과 강의를 전부 하지는 못했지만, 좀 더 Figma 인터페이스에 익숙해져서 빠르게 구현을 할 수 있도록 하여 각 구현의 시간을 줄여야 겠다는 생각이 드는 주였습니다.

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

hee j

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

목차Dropbox Clone ProjectDrag & Drop 할 영역 설정 및 서버에 파일 전송supabase의 Storage에 첨부파일 업로드첨부파일 검색, 삭제 2주차 미션파일의 마지막 수정(업로드) 시간을 표시하기파일명을 UUID로 변경하여 업로드하기Dropbox Clone Project1. Drag & Drop 할 영역 설정Drag & Drop 라이브러리 설치npm i --save react-dropzonefile-dragdropzone.tsx 파일div: 파일을 받는 영역 태그input: 파일 정보를 받는 태그isDragActive: 어떤 파일을 드래그 앤 드롭할 때 영역에 무엇을 보여줄지 정할 수 있게 해주는 값 ex) 드래그를 했다면? 파일을 여기에 드롭: 아니라면 파일을 드래그 앤 드롭을 해라 라는 문구 출력formData에 파일 이름과 파일 정보를 담아서 전송multiple을 true로 작성하여 여러 파일을 받을 수 있게 한다 export default function FileDragDropZone() { const uploadImageMutation = useMutation({ mutationFn: uploadFile, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ["images"], // images로 시작하는 것들 전부 리셋 }); }, }); const onDrop = useCallback(async (acceptedFiles) => { // 10개 이하의 파일만 업로드함 if (acceptedFiles.length > 0 && acceptedFiles.length <= 10) { const formData = new FormData(); acceptedFiles.forEach((file) => { formData.append(file.name, file); }); await uploadImageMutation.mutate(formData); } }, []); const { getRootProps, getInputProps, isDragActive } = useDropzone({ onDrop, multiple: true, }); return ( <div {...getRootProps()} className="w-full border-4 border-dotted border-blue-700 flex flex-col items-center justify-center py-20 cursor-pointer" > <input {...getInputProps()} /> {uploadImageMutation.isPending ? ( <Spinner /> ) : isDragActive ? ( <p>파일을 놓아주세요.</p> ) : ( <p>파일을 여기에 끌어다 놓거나 클릭하여 업로드 하세요.</p> )} </div> ); }  2. supabase의 Storage에 첨부파일 업로드.env 파일에 Storage 명 추가NEXT_PUBLIC_STORAGE_BUCKET=miniboxroot/actions 폴더 생성storageActions.ts 파일에 storage에 업로드할 함수 작성export async function uploadFile(formData: FormData) { const files = Array.from(formData.entries()).map( ([name, file]) => file as File ); // all(): 여러 파일을 한 번에 업로드 진행하기 위해 사용 const results = await Promise.all( files.map((file) => { supabase.storage .from(process.env.NEXT_PUBLIC_STORAGE_BUCKET) .upload(file.name, file, { upsert: true }); }) ); return results; 3. 첨부파일 검색, 삭제storageActions.ts 파일에 파일 검색, 파일 삭제 함수 추가export async function searchFiles(search: string = "") { const supabase = await createServerSupabaseClient(); const { data, error } = await supabase.storage .from(process.env.NEXT_PUBLIC_STORAGE_BUCKET) .list(null, { search }); // path, options, parameters handleError(error); return data; } export async function deleteFile(fileName: string) { const supabase = await createServerSupabaseClient(); const { data, error } = await supabase.storage .from(process.env.NEXT_PUBLIC_STORAGE_BUCKET) .remove([fileName]); handleError(error); return data; } 2주차 미션[미션 완성 이미지][파일의 마지막 수정(업로드) 시간을 표시하기]dropbox-image.tsxstorage에서 받아오는 정보 내에 마지막 수정 시간을 담고 있는 updated_at을 가져와 표시formData로 날짜와 시간의 가독성을 높여줌const formData = (dateString: string) => { return format(new Date(dateString), "yyyy-MM-dd HH:mm:ss"); }; return ( ... {/* FileName */} <div>{image.name}</div> <div>수정된 시간: {formData(image.updated_at)}</div> ... ) [파일명을 UUID로 변경하여 업로드하기]- 한글명 파일이 업로드 되지 않아 uuid로 파일 명을 변경하여 업로드 해보았음- 업로드는 잘 되나 같은 파일을 업로드 하여도 파일 명이 변경되어 업로드 되기 때문에 새로 업로드 되어 업로드 시간이 변경되지 않아 적용하지는 않음uuid 설치하기npm install uuidstorageActions.tsext 변수에 첨부 파일의 확장자 추출fileName 변수에 uuid+.확장자를 합쳐 파일명 생성const results = await Promise.all( files.map((file) => { const ext = file.name.split(".").pop(); // 확장자 추출 const fileName = `${uuidv4()}.${ext}`; // UUID 기반 파일명 생성 supabase.storage .from(process.env.NEXT_PUBLIC_STORAGE_BUCKET) .upload(fileName, file, { upsert: true }); }) );

풀스택풀스택next.jsreactsupabase워밍업발자국

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

Readable Code: 읽기 좋은 코드를 작성하는 사고법 학습 정리 및 회고객체 지향 설계와 테스트 코드 적용하기이번 주는 객체 지향 설계와 테스트 코드 작성법에 대해 집중적으로 학습했다. 그동안 객체 지향 프로그래밍을 단순히 클래스와 메서드를 사용하는 정도로만 이해하고 있었다. 하지만 이번 학습을 통해 SOLID 원칙, 일급 컬렉션, Enum 활용 등 객체 지향의 깊은 의미를 깨달았다.먼저 SOLID 원칙은 유지보수 가능한 코드를 작성하는 데 핵심이라는 것을 다시 한번 느꼈다. 특히 SRP(단일 책임 원칙)와 OCP(개방-폐쇄 원칙)를 적용하면서 기능 확장이 쉬워지고, 코드가 더욱 명확해지는 경험을 했다. 과거 프로젝트에서 하나의 클래스가 여러 책임을 지는 경우가 많았는데, 이때마다 코드 변경이 두려웠던 이유가 책임이 명확하지 않아서였다는 걸 알게 되었다.Enum과 일급 컬렉션도 실제로 적용해보니 생각보다 훨씬 유용했다. Enum을 통해 매직 넘버와 매직 스트링을 제거하니 코드 가독성이 크게 향상되었고, 일급 컬렉션을 사용해 컬렉션에 대한 처리를 클래스 내부로 위임했더니 로직이 깔끔해지고 유지보수가 쉬워졌다. 테스트 코드도 중요한 학습이었다. 이전에는 테스트 코드를 귀찮은 존재로 여기거나, 프로덕션 코드 작성 후 대충 작성하는 정도였다. 하지만 이번에 제대로 테스트 코드를 작성하면서 느낀 점은, 오히려 테스트 코드가 명확한 문서가 되고, 코드의 설계를 도와준다는 점이었다. 특히 TDD(Test Driven Development)를 통해 미리 테스트 케이스를 작성하니, 더 신중하게 설계할 수 있었고, 예외 상황도 명확히 구분하게 되었다.하지만 객체 지향과 테스트 코드 작성에 아직 어려운 점도 많다. 예를 들어 객체 간의 명확한 메시지 흐름과 책임 분리, 그리고 어떤 테스트 케이스가 적절한지 고민하는 부분이 쉽지 않다. 특히 지난 리팩토링 미션에서 if문을 줄이고 메서드를 추출하는 과정에서 억지스럽게 로직을 분리한 느낌이 들었다. 아직 객체지향적 사고 방식이 완전히 익숙하지 않다는 것을 느꼈다.앞으로의 목표는 다음과 같다.SOLID 원칙을 항상 염두에 두고 코드를 설계하기Enum과 일급 컬렉션을 적극적으로 활용하여 코드 품질 높이기TDD 방식을 꾸준히 연습하여 테스트 코드를 작성하는 습관 들이기지금까지 학습한 내용을 다시 복습하면서, 객체 지향과 테스트 코드 작성 능력을 꾸준히 성장시켜 나가겠다.

연유

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

✏ 학습회고 2주차 운영체제 강의에서는 프로세스와 메모리에 대해 깊이 배웠습니다. 강의를 들으며 공부한 내용을 기록했는데 집중도 잘되고 복습하기에도 편리했습니다. 다음주에는 3주차 학습과 함께 1주차와 2주차 내용을 복습하겠습니다.🎯 미션 운영체제 FIFO 스케줄링의 장단점이 뭔가요?장점은  단순하고 직관적인 것입니다.단점은 실행시간이 짧고 늦게 요청된 프로세스가 실행시간이 길고 빨리 요청된 프로세스의 작업을 기다려야하는 것입니다. 또한 I/O 작업이 있다면 CPU 사용률이 떨어집니다. SJF를 사용하기 여러운 이유가 뭔가요?프로세스가 얼마나 실행될 지 예측하기 힘듭니다. 그리고 Burst Time이 긴 프로세스의 경우 아주 오랫동안 실행되지 않을 수 있습니다. RR 스케줄링에서 타임 슬라이스가 아주 작으면 어떤 문제가 발생할까요?컨텍스트 스위칭이 빈번하게 발생하여 오버헤드가 너무 커집니다.  운영체제가 MLFQ에서 CPU Bound Process와 I/O Bound Process를 어떻게 구분할까요?타임 슬라이스를 오버하여 CPU를 강제로 뺏기면 CPU Bound Process로 구분하고, 스스로 CPU를 반납하면 I/O Bound Process로 구분을 합니다. 공유자원이란무엇인가요? 프로세스 간 통신을 할 때 공동으로 이용하는 변수나 파일입니다.  교착상태에 빠질 수 있는 조건은 어떤 것들을 충족해야할까요?상호배제, 비선점, 점유와 대기, 원형 대기 입니다.   자료구조와 알고리즘 재귀함수에서 기저조건을 만들지 않거나 잘못 설정했을 때 어떤 문제가 발생할 수 있나요?콜스택에 함수가 무한히 쌓여서  메모리가 부족해집니다. 스택 오버플로우가 발생하며 프로그램이 종료될 수 있습니다. 0부터 입력 n까지 홀수의 합을 더하는 재귀 함수를 만들어보세요.function sumOdd(n){ if(n == 0) return 0; if(n == 1) return 1; if(n & 1) { return sumOdd(n - 2) + n; } else { return sumOdd(n - 1); } } console.log(sumOdd(10)) // 25n이 홀수라면 n의 값을 더하고 sumOdd(n - 2)를 수행하도록 했습니다. 짝수라면 sumOdd(n - 1)을 수행하도록 했습니다.   다음 코드는 매개변수로 주어진 파일 경로(.는 현재 디렉토리)에 있는 하위 모든 파일과 디렉토리를 출력하는 코드입니다. 다음 코드를 재귀 함수를 이용하는 코드로 변경해보세요.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 traverseDirectory1(directory){ const files = fs.readdirSync(directory); for(const file of files){ const filePath = path.join(directory, file); const fileStatus = fs.statSync(filePath); if (fileStatus.isDirectory()) { console.log('디렉토리:', filePath); traverseDirectory1(filePath); } else { console.log('파일:', filePath); } } } traverseDirectory1(".");재귀함수를 사용하므로 stack을 사용하지 않아도 됨해당 파일이 디렉토리라면 traverseDirectory1() 를 재귀적으로 호출 📝회고 미션 과정에서 재귀함수를 하향식으로 구현하려고 노력했습니다. 자료구조와 알고리즘 3번 미션에서 for문을 재귀함수로 대체하려고 했으나 구현하지 못했습니다. 이유는 디렉토리에 여러 개의 파일이 있어서 반복해서 출력해야하기 때문입니다. 구현가능한 방법이 있다면 알고 싶습니다.

안지수

[워밍업 클럽] CS 전공지식 2주차 미션

운영체제1. FIFO 스케줄링의 장단점이 뭔가요? FIFO 스케줄링의 장점은 ‘먼저 들어온 작업이 먼저 나가는(First In First Out)’,스케줄링 큐에 들어온 순서대로 CPU를 할당받는 단순하고 직관적인 알고리즘이라는 것이고,단점은 한 프로세스가 완전히 끝나야 다음 프로세스가 시작되기 때문에실행시간이 짧고 늦게 도착한 프로세스가 실행시간이 길고 빨리 도착한 프로세스의 작업을 기다려야 한다는 점입니다.또 I/O 작업이 있을 경우 CPU는 I/O 작업이 끝날 때까지 쉬고 있기 때문에 CPU 사용률이 떨어지게 됩니다.2. SJF를 사용하기 여러운 이유가 뭔가요?SJF는 작업 시간이 짧은 프로세스부터 CPU를 할당하는 알고리즘으로,1) 어떤 프로세스가 얼마나 실행될지 예측하기 힘들고(프로세스의 종료시간을 예측하기 어려움),2) Burst Time이 긴 프로세스는 아주 오랫동안 실행되지 않을 수도 있기 때문에 사용하기 어렵습니다.3. RR 스케줄링에서 타임 슬라이스가 아주 작으면 어떤 문제가 발생할까요?컨텍스트 스위칭이 너무 자주 일어나게 되어 타임 슬라이스에서 실행되는 프로세스의 처리량보다 컨텍스트 스위칭을 처리하는 양이 훨씬 커져서 오버헤드가 큰 상황이 발생합니다. 4. 운영체제가 MLFQ에서 CPU Bound Process와 I/O Bound Process를 어떻게 구분할까요?CPU를 사용하는 프로세스가 CPU를 스스로 반납하면 CPU 사용이 적은 I/O Bound Process,CPU를 사용하는 프로세스가 타임 슬라이스 크기를 오버해서 CPU 스케줄러에 의해 강제로 CPU를 반납하게 되면 CPU를 많이 사용하는 CPU Bound Process로 구분합니다.5. 공유자원이란무엇인가요?프로세스 간 통신을 할 때 공동으로 이용하는 변수나 파일들입니다.6. 교착상태에 빠질 수 있는 조건은 어떤 것들을 충족해야할까요?- 상호배제 : 어떤 프로세스가 한 리소스를 점유했다면 그 리소스는 다른 프로세스에게 공유되면 안됩니다.- 비선점 : 프로세스A가 리소스를 점유하고 있는데 프로세스B가 리소스를 빼앗을 수 없어야 합니다.- 점유와 대기 : 어떤 프로세스가 리소스A를 가지고 있는 상태에서 리소스B를 원하는 상태여야 합니다.- 원형 대기 : 점유와 대기를 하는 프로세스들의 관계가 원형을 이룹니다.자료구조1. 재귀함수에서 기저조건을 만들지 않거나 잘못 설정했을 때 어떤 문제가 발생할 수 있나요?함수를 무한 호출하게 되어 스택오버플로우가 발생할 수 있습니다.2. 0부터 입력 n까지 홀수의 합을 더하는 재귀 함수를 만들어보세요.function sumOdd(n) { if (n <= 0) return 0; if (n % 2 == 0) n--; return n + sumOdd(n - 2); } console.log(sumOdd(10)); // 25 3. 다음 코드는 매개변수로 주어진 파일 경로(.는 현재 디렉토리)에 있는 하위 모든 파일과 디렉토리를 출력하는 코드입니다. 다음 코드를 재귀 함수를 이용하는 코드로 변경해보세요.const fs = require("fs"); const path = require("path"); function traverseDirectory(directory) { const files = fs.readdirSync(directory); // 현재 디렉토리의 파일 및 디렉토리 목록을 가져옴 for (const file of files) { const filePath = path.join(directory, file); const fileStatus = fs.statSync(filePath); if (fileStatus.isDirectory()) { console.log("디렉토리:", filePath); traverseDirectory(filePath); // 재귀 호출 } else { console.log("파일:", filePath); } } } traverseDirectory("."); // 현재 디렉토리부터 탐색 시작

soo97703

인프런 워밍업클럽 2주차 발자국

학습 내용 요약1. 객체 지향 적용하기1) 상속과 조합 - 조합을 사용하자. 상속은 수정이 어렵다.2) value object - 도메인의 어떤 개념을 추상화하여 표현한 값 객체불변성, 동등성, 유효성 검증을 보장해야한다.entity는 식별자가 존재한다는 점이 차이점이다.3) 일급 컬렉션 - 컬렉션을 포장하면서 컬렉션만을 유일한 필드로 가지는 객체4) Enum은 상수의 집합이며 상수와 관련된 로직을 담을 수 있는 공간2. 코드 다듬기1) 주석은 우리가 가진 모든 표현 방법을 총동원해 코드에 의도를 녹여내고 나서 그럼에도 전달해야할 정보가 남았을 때 사용하자2) 메서드는 public 메서드 → private 메서드 순으로 나열하는게 좋다.상태변경 → 판별 → 조회 메서드 순으로 나열!3) 패키지 나누기 - 패키지는 문맥으로써의 정보를 제공할 수 있다.  일주일 동안의 회고강의를 들으면서 노션에 내용을 정리했다.정해진 진도표에 따라서 강의를 듣지 못해서 미션도 제대로 수행하지 못했고 강의가 많이 밀렸다..미션 해결 회고혼자서 리팩토링을 하려고 보니, 어디서부터 어떻게 시작해야할지 모르겠고 너무 막막했다. 일단 이번 주에 강의를 다 듣지 못해서 완성도가 낮은 상태로 일단 제출을 했다. 그리고 value object, 일급 컬렉션 등에 대한 내용이 어렵다고 느껴져서 강의를 듣고 실습을 해본 후에 다시 혼자 리팩토링을 하는 연습을 해봐야겠다는 생각이 들었다. 인프런 강의 [Readable Code : 읽기 좋은 코드를 작성하는 사고법]을 보고 작성했습니다.https://www.inflearn.com/course/readable-code-%EC%9D%BD%EA%B8%B0%EC%A2%8B%EC%9D%80%EC%BD%94%EB%93%9C-%EC%9E%91%EC%84%B1%EC%82%AC%EA%B3%A0%EB%B2%95/dashboard

인프런워밍업클럽

강신욱

[인프런 워밍업 클럽 Full Stack 3기] 스터디 2주차

  미션 수행이번 미션에서는 실습에서 구현한 Dropbox에 마지막 업로드 시간을 표시하는 기능을 추가하는 것이 목표였다.구현 결과각 파일을 표시할 때 아래에 마지막 업로드 시간이 표시되는 것을 확인할 수 있다.2주차 회고2주차에는 중간점검 시간을 가졌다. 수강생들의 다양한 질문과 강사님의 상세한 답변을 통해 많은 것을 배울 수 있었던 유익한 시간이었다.특히, 1주차보다 Supabase의 사용법에 훨씬 익숙해진 것을 느낄 수 있었다. 하지만 여전히 실습 중 오타로 인해 코드가 실행되지 않는 상황이 반복되었다. 결국 깃허브에 제공된 코드나 강의 속 코드를 참고하며 오타를 수정하고 누락된 부분을 채워 넣어야 했다. 이러한 과정에서 실습 코드 오류를 해결하는 데 적지 않은 시간을 쏟게 되었다.자동완성 기능은 매우 편리하지만, 의도치 않은 코드 완성으로 인해 어디서 문제가 발생했는지 찾기가 어려운 경우도 많았다. 이 때문에 앞으로 실습에서는 더욱 신중하게 코드를 작성해야겠다고 다시 한번 다짐했다.이번 주차를 통해 실습 과정에서의 꼼꼼함과 디버깅의 중요성을 다시금 깨달았고, Supabase와 같은 외부 서비스를 활용하는 데 자신감을 조금 더 얻게 되었다.

강신욱

[인프런 워밍업 클럽 Full Stack 3기] 스터디 1주차

강의 주요 내용  서버 액션: 비동기 함수서버에서 실행되는 함수Next.js는 서버에서 실행되는 서버 컴포넌트와 브라우저에서 실행되는 클라이언트 컴포넌트를 구분하여 동작한다.파일 최상단에 'use server'가 선언되어 있으면 서버 컴포넌트이다.'use client'가 선언되어 있으면 클라이언트 컴포넌트이다.서버 컴포넌트 내에서 정의한 함수를 클라이언트에서 직접 호출할 수 있다. 즉, 별도의 API를 구현하지 않아도 클라이언트에서 서버 함수를 바로 사용할 수 있어 개발이 더욱 간편해진다.Recoil: 전역 상태 관리 라이브러리Recoil이란?Recoil은 클라이언트 상태 관리 라이브러리로, 여러 컴포넌트에서 동일한 상태를 공유할 수 있도록 도와준다. 예를 들어:뷰어 상태 관리헤더 검색창 상태 관리이처럼 다양한 상태를 효율적으로 관리할 수 있으며, Redux나 MobX보다 간편하게 사용할 수 있다.Recoil의 핵심 개념Recoil은 Atom과 Selector라는 두 가지 핵심 개념으로 구성된다.1. Atom전역 상태를 정의하는 가장 기본적인 단위여러 컴포넌트에서 공유 가능2. SelectorAtom 상태에서 파생된 값을 생성할 때 사용특정한 값만 변형하여 제공 가능예: 텍스트 상태에서 문자열 길이만 가져오는 Selector 정의Selector를 활용하면 불필요한 연산을 줄이고 최적화된 방식으로 상태를 관리할 수 있다.React Query: 클라이언트 상태 관리 및 데이터 동기화React Query란?React Query는 TanStack에서 개발한 서버 상태 관리 라이브러리로,서버에서 데이터를 가져오거나데이터를 변경하는 요청을 보낼 때 사용된다.React Query의 역할클라이언트에서 데이터 가져오기(Fetching)가져온 데이터 캐싱(Caching)서버 데이터 변경 시 동기화(Syncing)즉, 서버와 클라이언트 간의 데이터 흐름을 효율적으로 관리할 수 있도록 도와준다.React Query의 주요 장점✅ 자동 캐싱 (Auto Caching)✅ 서버 데이터와 클라이언트 데이터 분리 (Separation of Server & Client State)Supabase 주요 기능1. Table EditorTable Editor는 데이터베이스에서 직접 테이블을 생성 및 수정할 수 있는 기능이다.2. SQL EditorSQL Editor를 통해SELECT, UPDATE, DELETE 등의 SQL 쿼리를 실행할 수 있다.Assistant 기능 지원: AI가 DB와 연동되어 SQL을 자동 생성해준다.예: "이 테이블에서 특정 컬럼을 가져오고 싶어!"라고 입력하면, 적절한 SQL 쿼리를 자동 생성해준다.SQL이 익숙하지 않은 사용자에게 유용한 무료 기능이다.3. 데이터베이스 관리생성된 테이블 조회데이터베이스 함수 및 트리거 설정예: 특정 row가 생성, 수정, 삭제될 때 실행되는 트리거 설정 가능액세스 컨트롤(Role-based Access Control)특정 유저가 특정 테이블에 접근할 수 있도록 권한을 설정할 수 있다.(※ 액세스 컨트롤 관련 내용은 본 강좌 범위에서 제외)미션 수행이번 미션에서는 실습에서 구현한 TODO 리스트에 생성 날짜를 표시하는 기능을 추가하는 것이 기본 목표였다.처음 도전해보는 분야였던 만큼 시행착오가 많았고, 라이브러리 버전 호환 문제나 기타 충돌로 인해 많은 구글링이 필요했다.관련해서 강사님이 추천해주신 강의를 다시 한 번 정독한 뒤, 선택 과제도 수행해볼 예정이다.이번 실습을 통해 Next.js, Recoil, React Query, 그리고 Supabase를 더욱 깊이 있게 이해할 수 있었으며, 앞으로의 프로젝트에서도 적극적으로 활용해볼 계획이다.

풀스택

wlsgml450

인프런 워밍업 클럽 CS - 2주차 발자국 👣

운영체제프로세스 간 통신 ================== 프로세스는 독립적으로 실행되기도 하지만 다른 프로세스와 데이터를 주고받으며 통신을 하는 경우도 있다. 통신은 한 컴퓨터내에서 실행되고 있는 다른 프로세스와 할 수도 있고 네트워크로 연결된 다른 컴퓨터에 있는 프로세스와 할 수도 있다. 공유자원과 임계구역 ================== * 공유자원 : 프로세스 간 통신을 할 때 공동으로 이용하는 변수나 파일들이 있는데 이런 것들을 공유자원이라고 한다. * 임계구역 : 여러 프로세스가 동시에 사용하면 안되는 영역 교착상태의 필요조건 ================== 1. 상호배제 2. 비선점 3. 점유와 대기 4. 원형 대기 교착상태의 해결방법 (은행원 알고리즘) ================== 안전 상태를 유지하면서 자원을 할당하는 방법 메모리의 종류 ================== * 레지스터 > 캐시 메모리 > RAM(메인 메모리) > 보조 저장 장치(SSD, HDD) ✅ 속도: 레지스터가 가장 빠르고, 보조 저장 장치가 가장 느림 ✅ 용량: 보조 저장 장치가 가장 크고, 레지스터가 가장 작음 ✅ 가격: 속도가 빠를수록 비싸므로, 레지스터가 가장 비싸고, 보조 저장 장치가 가장 저렴함 메모리 할당방식 ================== * 가변 분할 방식 : 프로세스가 크면 메모리도 크게 할당 * 고정 분할 방식 : 프로세스 크기와 상관없이 메모리를 할당 * 버디 시스템 : 가변 분할 방식과 고정 분할 방식을 혼합해 단점을 최소화 자료구조와 알고리즘재귀함수 ================== 자기 자신을 호출하는 함수 * 기저 조건(탈출 조건): 기저 조건이 없으면 무한 호출로 콜스택 메모리가 가득 차 프로그램이 강제 종료됨 버블정렬 ================== 앞에 있는 숫자와 옆에 숫자를 비교해서 자리를 바꾸는 알고리즘 * 장점 : 이해와 구현이 간단 * 단점 : 성능이 좋지 않음... 선택정렬 ================== 배열에서 첫 번째 원소를 기준으로 마지막 원소까지 비교하여 가장 작은 값을 찾은 후, 첫 번째 원소와 교환하는 과정을 반복하여 정렬하는 알고리즘 * 장점 : 이해와 구현이 간단 * 단점 : 성능이 좋지 않음...  회고정보처리기사 자격증 시험을 준비할 때 봤던 내용들을 이번 인프런 워밍업 클럽을 통해 제대로 이해할 수 있어서 좋았다. 그때는 "뭐.. 뭐지? 일단 이해는 된 것 같은데.. 음..?" 이런 느낌이었는데, 감자 강사님의 그림을 통한 설명을 보면서 "아, 이게 이거구나!" 하고 이해되는 순간이 많았다. 이번 주 공부는 확실히 채워지는 기분! 앞으로도 기대된다. 그렇게 뿌듯해하던 와중에... 만난 재귀... 알고리즘... 재귀... 어렵다... 내용은 이해했지만, 직접 구현하는 건 아직 쉽지 않다. 중간 점검 시간에 감자 강사님께서 "재귀 함수는 처음엔 어렵지만, 많이 연습하면 적응된다." 라고 말씀해주셔서 위로가 되었다. 연습이 곧 길이다!!! 💪🔥 화이팅~  

Hyun Ho Kim

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

[Day 6]운영체제1⃣ 프로세스 간 통신1.1 프로세스 간 통신(Inter Process Communication, IPC)프로세스 사이에 서로 데이터를 주고받는 행위, 방법, 경로를 IPC라한다.프로세스가 통신이 가능하다는 것은 서로 다른 프로세스가 데이터를 주고 받을 수 있다는 것이다.이는 컴퓨터 내부에서의 프로세스 간 소통일수도 있고, 다른 컴퓨터 간에 프로세스 간 소통일 수도 있다.1.2 프로세스 통신의 종류한 컴퓨터 내의 프로세스(운영체제영역)간의 방식과 다른 컴퓨터 간 프로세스(네트워크 이용)간의 방식으로 나뉜다.✔ 운영체제에서 지원하는 방식파일파이프✔ 네트워크를 사용하는 방식소켓(tcp, udp 등)RPC(Remote Procedure Call) - 원격 프로시저 호출1.2.1 프로세스 통신의 종류 상세내용 (+추가내용)파이프통신을 위한 메모리 공간을 생성하여 프로세스가 데이터를 주고 받게끔 한다.한 프로세스가 쓰고 다른 프로세스가 읽는 선입선출(FIFO) 형태의 큐라고 할 수 있다. 이는 단방향 통신이 가능한 파이프의 특징 때문에 반이중(Half-Duplex) 통신이라고 부르기도 한다.익명 파이프와 네임드 파이프 두 종류로 나뉜다.익명 파이프(Anonymous Pipe)✅부모-자식, 형제 등 통신 대상이 정해진 두 프로세스가 단방향 통신(Half Duplex)할 때 사용한다.✅양방향 통신(Full duplex)을 위해선 익명 파이프가 두 개 필요한데, 이는 구현이 복잡하다.네임드 파이프(Named Pipe)✅Anonymous Pipe가 확장된 형태로, 파일 시스템에 이름이 부여된 파이프를 말함.✅FIFO라는 통신을 위한 파일을 만들고, 이를 통해 양방향 통신이 가능하다. 소켓네트워크 소켓 통신을 통해 데이터를 공유한다.양쪽 PC에서 각각 임의의 포트를 정하고 해당 포트 간의 대화를 통해 데이터를 주고받는 형식이다.양방향 통신이 가능하다. 2⃣ 공유자원과 임계구역한정된 자원을 가지고 프로세스가 동시에 작업할 경우 동기화에 문제가 생길 수 있다.2.1 공유자원으로의 접근공유 자원은 여러 프로세스가 공동으로 이용하는 변수, 메모리, 파일 등을 말한다.이는 각 프로세스 접근 순서에 따라 그 결과가 달라질 수 있다.2.2 경쟁 조건(Race Condition)경쟁 조건은 2개 이상의 프로세스가 공유 자원을 병행적으로 읽거나 쓰는 상황을 말하며, 공유 자원 접근 순서에 따라 실행 결과가 달라지는 상황을 말한다.2.3 임계구역여러 프로세스가 데이터를 공유하며 수행될 때, 각 프로세스에서 공유 데이터를 접근(Access)할 수 있도록 하는 프로그램 코드 부분이다.여러 프로세스가 동일 자원을 동시에 참조하며 값이 바뀔 위함 가능성이 있는 영역이다.2.3.1 임계구역 해결 조건상호 배제(Mutual exclusion)한 프로세스가 임계구역에 들어가면 다른 프로세스는 임계구역에 들어갈 수 없다.2.4 세마포어에츠허르 다익스트라가 제안한 교착상태(DeadLock)에 대한 해법으로 두 개의 원자적(Atomic) 함수로 제어되는 정수 변수로 멀티프로그래밍 환경에서 공유자원에 대한 접근 제어를 하는 방법으로 사용된다.1개의 공유되는 자원에 제한된 개수의 프로세스 또는 쓰레드만 접근할 수 있도록 한다.2.4.1 작동 원리세마포어의 작동 원리는 상호 배제 알고리즘에 기반한다.<aside> 💡상호 배제 알고리즘(Mutual Exclusion Algorithm)동시 프로그래밍에서 공유 불가능한 자원의 동시 사용을 피하기 위해 사용되는 알고리즘, 임계 구역에서 구현된다.</aside>일반적으로 세마포어의 값이 0이면 자원에 접근할 수 없도록 블럭하고 0보다 크면 접근함과 동시에 세마포어의 값을 1 감소시킨다.반대로 종료하고 나갈 때에는 세마포어의 값을 1 증가시켜 다른 프로세스가 접근할 수 있도록 한다. 알고리즘1⃣ 재귀(Recursion)어떠한 것을 정의하는 과정에서 자기 자신을 참조하는 것1.1 특징재귀함수를 구현할 때 탈출 조건(기저 조건)을 정의하지 않으면, 콜스택에 계속 쌓이게 된다.스택 오버플로우(Stack Overflow) - 콜스택에 메모리 용량을 초과하여 프로세스가 강제 종료되는 현상팩토리얼(Factorial) - 재귀함수를 사용하면 쉽게 해결 가능한[ Day 7 ]운영체제1⃣ 교착상태두 개 이상의 프로세스나 스레드가 서로 자원을 얻지 못해서 다음 처리를 하지 못하는 상태를 말한다.즉, 무한히 다음 자원을 기다리게 되는 상태이다.시스템적으로 한정된 자원을 여러 곳에서 사용하려고 할 때 발생한다.1.1 교착상태의 발생 이유✔ 공유자원 1.2 교착상태의 필요조건한 가지라도 충족하지 않는다면 교착상태는 발생하지 않는다.상호배제한 프로세스가 리소스를 점유했다면 그 리소스는 다른 프로세스에게 공유되면 안된다.비선점사용하고 있는 리소스를 다른 프로세스가 뺏을 수 없다.점유와 대기원형대기점유와 대기 상태가 원형 상태인 것을 의미한다.2⃣ 교착상태 회피전체자원의 수와 할당된 자원의 수를 기준으로 안정상태와 블안정상태로 나뉜다.불안정상태에 있더라도 무조건 교착상태에 빠지는 것은 아니다.2.1 은행원 알고리즘교착상태(DeadLock)를 방지하는 자원 할당 알고리즘이다.은행의 대출 방식에서 유래되었는데, 은행이 고객에게 대출을 해줄 때 보유 잔고를 고려하듯, 프로세스에 자원을 할당할 때 시스템이 안정상태를 유지할 수 있는지 판단하는 방식이다. 알고리즘1⃣ 재귀적으로 생각하기재귀함수는 하위 문제를 기반으로 현재 문제를 해결하는 하향식 계산을 주된 방식으로 사용한다.1.1 배열의 합1.2 문자열 길이 계산// 구하려는 문저열 마지막 원소를 제외한 나머지 function strlength(arr){ if(arr[0] == null) return 0; return strlength(arr.slice(0, -1)) +1);1.3 지수함수function power(x, n) { if(n==0) return 1; return power(x, n-1) * x;[ Day 8 ]운영체제1⃣ 컴파일1.1 프로그래밍 언어컴파일 언어개발자가 코드를 작성하고 컴파일하여 0과 1로 된 기계어로 실행파일을 만든다.개발자가 문법 오류를 일으켰는지 검사를 한다.인터프리터 언어개발자가 작성한 코드를 미리 기계어로 만들지 않고 실행 시 코드를 한 줄 씩 해석해 실행한는 언어미리 검사를 하지 않기 때문에 실행할 때 오류가 날 수도 있고 속도도 컴파일과 비교하면 느리다.1.2 프로세스의 메모리 구조코드, 데이터, 힙, 스택으로 나뉜다.코드실행해야 할 코드가 들어가는 영역데이터전역변수나 배열이 들어가는 영역스택과 힙 영역은 프로세스가 실행될 때 할당되는 메모리로스택지역변수와 함수 관련 값들이 들어감힙실행 중에 메모리 공간을 할당할 수 있는 유동적인 공간이다.1.3 컴파일 과정✔ 컴파일 과정전처리헤더파일 혹은 매크로를 치환하여 .i파일로 저장 (#include, #define 등)컴파일어셈블리어로 컴파일 후 .s 파일로 저장 ( c, c++ )어셈블어셈플리어를 링커가 읽을 수 있는 목적파일로 변환하여 .o 파일로 저장 (기계어로 최종 번역)링킹목적파일들을 하나로 묶어 실행파일 알고리즘하노이의 탑1883년 프랑스의 수학자 Édouard Lucas가 처음으로 발표한 게임으로, 재귀함수 예제로 활용된다.  [ Day 9 ]운영체제메모리의 종류✔휘발성 메모리정보를 일회성으로 저장하고 삭제하는 메모리를 말한다. 1.1 RAM휘발성 메모리의 대표적인 형태, CPU와의 빠른 데이터 교환을 위해 사용된다.프로그램 실행, 작업 데이터를 임시로 저장하여 CPU가 빠르게 접근할 수 있게 하는 역할을 한다. 1.2 레지스터CPU 내부에 위치한 가장 빠른 메모리로, 데이터나 명령어의 임시 저장소 역할을 한다.CPU에서 바로 처리하는 데이터, 즉 명령어 실행 중에 필요한 데이터를 빠르게 저장하고 불러오는데 쓰인다. 1.3 캐시 메모리(Cache memory)CPU와 주 메모리(RAM)간의 속도 차이를 줄이기 위해 사용되는 고속 메모리자주 사용되는 데이터를 미리 저장하여 CPU의 처리 속도를 향상시키는 역할을 한다. ✔ 가상 메모리RAM + HDD 알고리즘✔정렬 알고리즘(Sorting Algorithm)정렬이란 데이터를 오름차순이나 내림차순으로 배치하는 것을 말한다.정렬 시 데이터를 순서에 맞게 배치에 보다 더 쉽고 빠르게 검색할 수 있다. 1.1 버블 정렬서로 인접한 두 요소를 비교하여 정렬하는 알고리즘인접한 2개의 요소를 비교해서 교환하는 형태이다.0(n^2)의 시간복잡도를 가진다.1.2 선택 정렬배열에서 최솟값을 찾아 맨 앞부터 둔다.일반적으로 데이터의 교환이 이동 작업보다 복잡하다.O(n^2)의 시간복잡도를 가진다. 2주차를 회고하며..어떻게 따라가고 있는지도 모르게 주말이 다가왔고 솔직히 내용들을 완벽히 이해한 것 같지는 않다.발자국을 작성하며 다시 복습을 함으로써 조금은 머릿속에 정리되는 듯 하다.그러나 신기한게 감자님의 강의가 그림으로 쉽게 알려주시다 보니 당시에 강의해주셨던 그림들이 스쳐지나가며 생각이 난다.재귀와 하노이의 탑 등 다시 스스로 코드를 작성하며 복기해봐야겠다.

Hyun Ho Kim

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

1. 재귀함수에서 기저조건을 만들지 않거나 잘못 설정했을 때 어떤 문제가 발생할 수 있나요?스택 오버플로가 발생합니다.콜스택에 스택 프레임이 쌓이는데, 기저조건을 만들지 않으면 함수가 무제한 호출되며, 스택 프레임도 무제한으로 쌓이게 됩니다.콜스택 메모리의 잉여 용량을 초과하면 프로세스가 OS에 의해 강제 종료되는데 이를 스택 오버플로라고 합니다.  2. 0부터 입력 n까지 홀수의 합을 더하는 재귀함수를 만들어보세요.function sumOdd(n) { if (n <= 0) return 0; // 현재 숫자가 홀수면 더하고, 짝수면 그냥 넘긴다 if (n % 2 !== 0) { return n + sumOdd(n - 1); } else { return sumOdd(n - 1); } } console.log(sumOdd(10)); // 25 3. 다음 코드는 매개변수로 주어진 파일 경로(.는 현재 디렉토리)에 있는 하위 모든 파일과 디렉토리를 출력하는 코드입니다. 다음 코드를 재귀 함수를 이용하는 코드로 변경해보세요. 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("."); // 현재 경로의 모든 하위 경로의 파일, 디렉토리 출력 const fs = require("fs"); // 파일을 이용하는 모듈 const path = require("path"); // 폴더와 파일의 경로를 지정해주는 모듈 function recursiveTraverseDirectory(directory) { const files = fs.readdirSync(directory); for (const file of files) { const filePath = path.join(directory, file); const fileStatus = fs.statSync(filePath); if (fileStatus.isDirectory()) { console.log('Directory:', filePath); recursiveTraverseDirectory(filePath); } else { console.log('File:', filePath); } } } recursiveTraverseDirectory("."); // 현재 경로의 모든 하위 경로의 파일, 디렉토리 출력

채널톡 아이콘