블로그

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

💻강의 수강<백엔드>[실습] 데이터베이스 초기화[실습] 리포지토리 개발[실습] 리포지토리 테스트 코드 작성[실습] 리포지토리 성능 개선[실습] 클래스 생성[실습] DTO 개발[실습] 리포지토리 개발[실습] 서비스 개발[실습] 서비스 테스트 코드 작성[실습] 컨트롤러 개발[실습] 컨트롤러 테스트 코드 작성<프론트엔드>[실습] Thymeleaf - 부트스트랩 템플릿[실습] Thymeleaf - 템플릿 수정(index)[실습] Thymeleaf - 템플릿 수정(resume)[실습] Thymeleaf - 템플릿 수정(projects)[실습] Thymeleaf - 템플릿 수정(레이아웃)[실습] 인터셉터 개발 💻오류..<1주차 강의 진도>[개발] 리포지토리 테스트코드 작성testFindAllByIsActive 테스트코드 실행 오류본 테스트코드 내부의 experiences의 size 값이 계속 0으로 나와 코드를 점검하였는데, Experience 엔터티의 addDetails 메소드의 매개변수명으로 details가 아닌 detail이 들어가 있어 함수 내부의 details와 매칭이 되지 않아 addDetails 함수가 제대로 작동하지 않았고, 그래서 Experience 배열의 한개의 Experience 인스턴스 아래의 ExperienceDetail 배열에 값이 추가되지 않았던 것이었다... 30분동안 이곳저곳 찾아보다가 도저히 찾지못해 멘붕 오기 직전 다시 한번 작성한 코드 전부를 단어 단위로 하나하나 꼼꼼하게 읽었는데, 저 곳에 s 하나가 빠져있던걸 깨닫고 벙쪘었다. 역시 개발자는 자나깨나 오타를 조심해야한다  💟미션이번주 과제는 본격 개발.. REST API 작성과 조회 API 구현이다  <3/10 월 미션3 REST API 작성>처음엔 분명 학생-소속학과-학과 이렇게 세 엔터티로만 할 예정이었는데.. 내가 학사 시스템에서 가장 많이 쓰는 수강신청 기능(개설강의조회, 신청, 삭제)을 구현하려다 보니 강의-수강강의가 필요했고 강의에 교수 정보도 들어가야 하니 교수 엔터티도 추가해야 했다. 이 시스템의 로그인을 생각하며 학생의 정보는 어느 시점에 들어가야하나? 를 고민했는데 보통 학생의 정보는 학생이 원서를 넣을때 거의 모든 정보가 들어간다는 걸 떠올리고 원서 테이블마저 만들게 되었다.😑원서 데이터가 학생 데이터로 옮기는 시점 또한 고민이 되었는데, 이 때문에 관리자 기능도 필수가 되었다. 원서를 조회하고 합격자(본격적으로 하려면 합격자 산정 로직을 넣어야 할 것 같은데 이건 이 미니미니 프로젝트를 사이드 사이즈로 키울 결심히 들 때..?)를 합격 상태로 바꿨을 때가 그 시점이다. 그리고 여기서 1안 2안이 나뉘는데, 우선 1안은 상태를 합격으로 바꾸면서 트랜잭션 처리 넣어 동시에 학생 테이블로 데이터가 옮겨진다. 그러면 학생이 합격자 발표 조회를 했을때 학생 테이블에 자기 정보가 있다면 합격 조회되고 put으로 비밀번호만 만들어서 로그인하기. 2안은 관리자는 원서 상태만 합격으로 바꾸고 학생이 합격자 발표 후 자기 원서의 상태가 합격이라면 그 때 비밀번호만 받으면서 학생 테이블에 원서 테이블의 모든 정보 옮겨적기 시점의 문제고 성능과 효율성은 분명 비슷할 것 같은데.. 이런 쓸데없는게 제일 고민된다. 😂😂이런 느낌이 되었다 <3/13 목 미션4 조회 REST API 만들기>이번주 너무 바빠서 관리자모드 - 강의 조회 하나를 겨우 만들었다. 자바만 쭉 쓰다가 오랜만에 정석으로 dto로 포장하고 jpa로 개발하려니 여기저기 실수가 많아 달랑 이거하는데 오류 잡느라 시간이 많이걸렸다 게다가 타임리프도 하도 오랜만에 쓰니 하나도 기억이 안나 강의 열심히 들으며 만들었다. css 너무 싫다 완성 화면은 이런 느낌....... 템플릿은 포트폴리오에서 쓴 거 재사용했다 ; admin 템플릿을 다 가져오기엔 너무 사이즈가 크고 그정도까진 사용되지 않기때문에 ; ; null은 교수아이디와 학과아이디인데 아직 강의에 학과, 교수 테이블 조인을 안걸어서 우선은 null로 처리함  

백엔드스프링부트

천준민

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

📖 강의 요약 1⃣ 코드 다듬기 주석의 양면성"좋은 주석"우리가 가진 모든 표현 방법을 총 동원해 코드에 의도를 녹여 내고, 그럼에도 불구하고 전달해야 할 정보 가 남았을 때 사용(메서드,변수,클래스) 네이밍에서 의도가 드러나면 주석을 대신 할 수 있을 것 같다. 변수와 메서드의 나열 순서- 변수는 사용하는 순서대로 나열 - 공개 메서드 끼리 기준을 가지고 배치하는 것이 좋음 - 또한 공개 메서드의 배치에 따라서 비공개 메서드의 순서도 고려 해야 한다. - 상태 변경 >> 판별 >= 조회 "결론"나열 순서로도 의도와 정보를 전달 할 수 있다는 것 패키지 나누기"결론"패키지는 문맥으로써 정보를 제공할 수 있다. 2⃣ 기억하면 좋은 조언들 복잡하거나 엉망인 코드를 이해하려 할때 리팩토링 하면서 읽기공백으로 단락 구분하기필요한 적정 수준보다 더 높은 수준의 엔지니어링이 아닌지 생각해보기 Practical Testing: 실용적인 테스트 가이드 1⃣ 테스트는 왜 필요할까커버할 수 없는 영역 발생, 경험과 감에 의존, 늦은 피드백, 유지보수 어려움"올바른 테스트 코드"자동화 테스트로 비교적 빠른 시간안에 버그를 발견할 수 있고, 수동 테스트에 드는 비용을 크게 절약할 수 있다.가까이서 보면 느리지만 멀리서 보면 빠르다. 2⃣ 단위 테스트 작은 코드 단위를 독립적으로 검증하는 테스트프레임 워크 : Junit / 라이브러리 : AsserJ테스트 코드를 작성 할 때는 해피 케이스만 하는 것이 아니라 예외케이스도 함께 작성 해야한다. 3⃣ TDD : Test Driven Development4⃣"TDD"프로덕션 코드 보다 테스트 코드를 먼저 작성해 테스트가 구현 과정을 주독하도록 하는 방법론 = 테스트 주도 개발선 테스트 - > 기능 구현 💡 미션 [섹션 7. 리팩토링 연습]의 "연습 프로젝트 소개" 강의를 보고, '스터디 카페 이용권 선택 시스템 프로젝트에서 지금까지 배운 내용을 기반으로 리팩토링을 진행해 봅시다' 미션 제출 url : https://github.com/2unmini/readable-code/tree/mission/day7 이번 미션에 대한 목표는 배운 내용+ 내가 알고 있는 내용을 가지고 생각해 봤을 때 이유가 충분 한지 판단하여 리팩토링을 해보았지만 다음 날 결과 비교를 해보았을 때는 많이 달랐지만 리팩토링 과제를 하는 동안 이유에 대해 많이 생각해보는 시간이였다. 💬 회고 👍 잘한 점인프런 가입 이후로 처음으로 강의 완료율 100% 달성일급 컬렉션을 적용하여 리팩토링 한점😮‍💨 아쉬웠거나 보완하고 싶은 점System.out.println()을 메소드를 추출할려고 생각했던 점이 아쉬웠다.배운 내용을 다 적용하지 않고 상황에 맞게 적용해 보고 싶다. 📎출처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

백엔드3기backend워밍업클럽인프런

윤기숙

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

📖 2주차 - 학습 내용 정리1. 아이콘Union + Flatten Selection앤트맨 전략으로 확대후 수정 그 이후 사이즈 조정해서 아이콘을 컴포넌트화Icon placeholder를 미리 만들어 놓고, 사이즈나 다른 아이콘으로 변경한다.2. 인풋 컴포넌트버튼 (button, icon button, button stack)체크박스(check), 라디오(radio), 스위치(switch) 텍스트 라벨(label), 컨트롤 그룹 (control group), 텍스트필드(text field), 텍스트 영역(text area)셀렉트/드롭다운(select/drop down)Multi-Edit 기능의 신세계를 배웠다. Q로 같은 구조를 한거번에 선택하거나 shift를 통해 선택.3. 디스플레이 컴포넌트아바타(avatar), 아코디언(accordion), 구분선(divider)뱃지(badge), 칩(chips), 툴팁(tooltips)카드(basic cards), 테이블(table)  💡 2주차 - 회고 및 느낀 점 😆 좋았던 점컴포넌트를 하나하나 만들어가면서 확실하게 컴포넌트 베리어블 개념을 익힐 수 있었던 것.이런 것까지 만든다고? 라는 생각이들 정도로 자주 쓰이는 부분을 컴포넌트화하면서 정리해 볼 수 있었던 것.피그마에 잘 몰랐던 부분, 기본 개념들 반복하며 훈련하기, 그리고 유용한 단축키등을 다시 한번 정리할 수 있었던 것.😅 아쉬웠던 점이전에 Hug / Fill Container 부분을 아직 확실히 이해하지 못했던 부분들이 있었구나 깨닫는다. Basic cards만들 때, 조금 어려웠던 부분이 있었다. 앞으로 이런 부족함을 좀더 확실히 익힐 때까지 노력해야겠구나 그런 생각이 들었다.  😍 앞으로 바라는 점미션을 정해진 시간에 따라가기가 꽤 많은 양이라는 생각이 들었다. 밀리지 않도록 최선을 다해 따라가자.포기하거나 미루지 말고 완주를 위해 꾸준히 노력하자.

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

👣 리팩토링을 대하는 자세와 단위 테스트를 대하는 자세

🎯 코드의 의도를 명확하게 표현하기코드를 작성할 때 가장 중요한 요소중 하나는 “코드가 무엇을 하는지”명확하게 나타내는 것.코드 자체가 설명적이여야 하며, 주석에 의존하지 않아야 한다코드는 변한다. 즉 소프트웨어는 발전하며 코드는 동적이지만 주석은 변하지 않는다.좋은 네이밍은 코드의 가독성을 높이며 함축적인 의미를 담는 것이 중요하다.그렇다고 해서 너무 함추적인 의미를 담거나 줄임말은 피해야 한다.함수와 변수를 의미 있는 이름으로 정의하면 코드를 읽는 입장에서도 쉽게 이해할 수 있다.한개의 함수는 한가지 책임만 가져야 하며, 여러가지 기능을 포함하지 않도록 해야한다.🎯 비즈니스 로직이 자연스럽게 녹아있는가?비즈니스 로직은 도메인의 개념을 반영해야 한다현재 내 프로젝트의 요구사항 및 도메인 개념이 들어가 있어야 한다.도메인 용어를 코드에서 그대로 활용하는 것이 가독성 향상에 도움이 된다.🎯 변수와 메서드의 배치는 읽는 흐름을 고려관련된 변수와 메서드는 가까운 곳에 배치하는 것이 좋다.상위 개념에서 하위 개념으로 자연스럽게 흐르도록 정리해야 한다.공개메서드의 배치도 중요도롤 따라서 정리한다 → 객체의 상태를 변경하는 메서드는 상위에, 그 다음 판별 메서드 그 다음 조회 메서드 순으로 중요도를 나눠 배치하자.비공개 메서드는 공개메서드의 언급된 순서대로 배치하고, 공통으로 사용하는 메서드라면 하단에 두자.🎯 테스트 하기 쉬운 영역과 어려운 영역 나누기순수 함수 중심으로 설계하고, 테스트가 어려운 영역은 Mocking 활용하기테스트 하기 쉬운 영역구분하기순수함수(특정 입력이 특정 출력을 내는 경우)외부 의존성이 없는 메서드테스트 하기 어려운 영역DB 네트워크, 파일 입춥력 등 외부 시스템을 사용하는 코드static 또는 private 메서드환경 변수에 의존하는 코드외부 시스템을 사용하는 코드의 단위테스트는 Mock → 가짜 객체를 이용하여 의존성을 주입받고 테스트를 진행할 수 있다.📚 회고리팩토링은 필수가 아니라 “습관"이다.리팩토링은 “나중에 하는 것이 아니라, 항상 개선할 수 있는 습관” 으로 가져가야 한다.코드가 동작한다고 해서 “좋은 코드”가 아니다. 더 읽기 쉬운 코드 , 유지보수 하기 쉬운 코드가 되어야 한다.리팩토링에 있어서의 개발자의 태도“이 정도면 되겠지?”에서 멈추는 것이 아닌 “이 코드가 우리 프로젝트의 도메인 개념을 잘 설명하고, 가장 직관적인가?”를 고민하자리팩토링은 가독성을 최우선으로 둔다가독성이 높은 코드는 유지보수가 쉽고, 버그를 줄일 수 있다리팩토링을 할 때는 테스트가 필수이다단위 테스트 없이 리팩토링하면 코드가 망가졌는지 확인할 방법이 없다.그리고 무섭다. 단위 테스트 코드 없이 리팩토링 하는 것 자체가 무섭다.단위 테스트가 없는 코드를 리팩토링한다면 그 전에 먼저 테스트를 작성하자.리팩토링을 할 때는 단계적으로 진행하자변경할 때마다 단위 테스트를 실행하여 문제가 없는지 확인해야 한다.단위 테스트는 개발자로서 “코드 품질을 지키는 무기”다.단위 테스트는 단순한 체크리스트가 아니라 개발자가 “코드를 망가뜨리지 않도록 보호”하는 방패라고 생각하고 작성하자단위 테스트가 잘 갖춰진 프로젝트는 리팩토링이 자유롭다.실제로 나는 TDD보다 기능을 먼저 개발한 뒤 해당 기능이 내가 예상한대로 잘 작동하는지 안하는지 테스트 코드를 작성하는데, 다양한 케이스를 보호하는 테스트 코드를 작성하지 못하더라도, 허접한 테스트 코드라도 있는것이 낫다고 생각한다. 그 허접한 테스트 코드가 나를 살려줄 때도 많이 있었기 때문…그리고 오히려 테스트 코드가 없다면 디버깅에 더 많은 시간이 소요된다. BDD 스타일로 시나리오를 확인한뒤 테스트 코드를 보면서 데이터의 흐름을 유추할 수 있다. 🗣 개발자의 마음가짐✅ 테스트는 문서다.테스트는 코드의 사용법을 가장 정확하게 설명한다.테스트는 코드의 동작을 명확하게 문서화한다. 어떤 상황에서 어떻게 동작한다와 같은 동작을 명확하게 녹여낸다.테스트는 코드의 유지보수를 돕는 최신 문서다.✅ 리팩토링을 대하는 자세리팩토링은 코드 품질을 높이는 습관이다.코드의 가독성을 최우선으로 둔다.테스트 없이 리팩토링하지 않는다.과한 추상화를 피하고 실용적인 접근을 한다.✅ 단위 테스트를 대하는 자세단위 테스트는 “개발자의 무기”다.실패하는 테스트를 통해 버그를 예방한다.Happy Case + 예외 Case를 모두 테스트한다.테스트가 어려운 경우, Mocking을 활용한다.

백엔드워밍업클럽3기BE

김태영

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

[2주차] [섹션 6]주석의 양면성후대에 전해야 할 "의사 결정 히스토리"를 코드로 표현할 수 없을 때, 주석으로 상세하게 설명한다.주석을 작성할 때, 자주 변하는 정보는 최대한 지양해서 작성한다.관련 정책이 변하거나, 코드가 번경되었다면 주석도 잊지 않고 업데이트주석이 없는 코드보다, 부정확한 주석이 달린 코드가 치명적이다.좋은 주석우리가 가진 모든 표현 방법을 동원해 코드에 의도를 녹여내고, 그럼에도 불구하고 전달해야 할 정보가 남았을 때 사용하는 주석변수와 메서드의 나열 순서변수는 사용하는 순서대로 나열한다.메서드의 나열 순서는 개인 취향객체는 협력을 위한 존재, 외부 세계에 내가 어떤 기능을 제공할 수 있는지 드러냄공개 메서드를 상단에 배치공개 메서드끼리도 기준을 가지고 배치하는 것이 좋다중요도 순, 종류별로 그룹화하여 배치하면 중복된 로직을 만드는 것을 피할 수 있다.상태 변경 >> 판별 >= 조회 메서드패키지 나누기패키지는 문맥으로써의 정보를 제공할 수 있다.Level 패키지 안의 Beginner, Middle 등에 Level을 붙이지 않아도 되는 이유는 문맥상 레벨 단위라고 생각할 수 있기 때문패키지를 쪼개지 않으면 관리가 어려워진다.패키지를 너무 잘게 쪼개면 그것대로 관리가 어려워진다.대규모 패키지 변경은 팀원과의 합의를 이룬 시점에 하자.다같이 사용하는 패키지를 변경하면, 충돌이 발생할 수 있다.처음부터 잘 고민해서 패키지를 나누자!기능 유지보수하기 (1) - 버그 잡기기능을 개발하다 보면 리팩토링 과정에서 달라진 부분에 따라 수정해야 하는 경우가 생긴다.리팩토링을 진행하며 판별 조건이 세분화될 경우 세분화된 경우에 따라 수정해야 한다.기능 유지보수하기 (2) - 알고리즘 교체하기사용하는 알고리즘에 따라 시간 복잡도를 감소시킬 수도 있고, 메모리 사용을 감소시킬 수도 있다.재귀 대신 스택을 사용할 수도 있고, 그리디 방법에서 다이나믹 프로그래밍을 사용하여 감소시킬 수도 있다.IDE의 도움 받기코드 포맷 정렬 Ctrl + Alt + L 코드 품질에 도움을 주는 플러그인포맷 규칙을 설정할 수 있는 파일[섹션 7]스터디 카페 코드 리팩토링 연습중점으로 리팩토링한 것SRP (단일 책임 원칙)OCP (개방 폐쇄 원칙)LSP (리스코프 치환 원칙)추상화 레벨 맞추기getter 사용 지양판별 메서드 사용중복 제거, 인터페이스 활용 내가 놓친 것DIP (의존성 역전 원칙)핸들러 통합의미있는 내용 상수화일급 컬렉션 활용 [섹션 8]능동적 읽기복잡하거나, 엉망인 코드를 읽고 이해하려 할 때, 리팩토링하면서 읽기리팩토링 후, 돌아갈 수 있는 git reset --hard가 있다.핵심 목표는 도메인 지식을 늘리고, 이전 작성자의 의도를 파악하는 것오버 엔지니어링필요한 적정 수준보다 더 높은 수준의 엔지니어링ex) 구현체가 하나인 인터페이스구현체가 하나라면 굳이 인터페이스로 만들 필요가 없다. 근시일 내로 추가된다면 좋다.ex) 너무 이른 추상화추상화로 인해 정보가 숨겨지기 떄문에 복잡도는 증가, 이해도는 감소후대 개발자들이 선대의 의도를 파악하기 힘들다.은탄환은 없다은탄환은 없다 : 만능 해결사는 없다.클린코드가 무조건 좋은가?유지보수성은 좋지만, 변경성이 없는 코드라면 동작면에서 달라지는 게 없다.실무 : 2가지 사이의 줄다리기지속 가능한 소프트웨어의 품질 VS 기술 부채를 안고 가는 빠른 결과물모든 기술과 방법론은 적정 기술의 범위 내에서 사용되어야 한다.ex) 급한 배포인데, style 관련 리뷰를 주고, 고치도록 하는 사람..?도구라는 것은, 일단 그것을 한계까지 사용할 줄 아는 사람이 그것을 사용하지 말아야 할 때도 아는 법이다.[섹션 9]강의 마무리추상 (抽象)전문가는 언제나 탑다운으로 깔끔하게 생각할 것이다.추상 -> 구체로 생각하자  테스트 강의 [섹션 1]어떻게 학습하면 좋을까?선택과 집중모든 것을 잘할 수는 없다.[섹션 2]테스트는 왜 필요할까?기능 개발 중 이전 개발 영역과 겹치는 부분이 생긴다. (중복되는 부분)기존 코드를 건드리게 된다면 여전히 정상 작동하는지 검증이 필요하다.사람이 검증을 한다면?커버할 수 없는 영역 발생경험과 감에 의존늦은 피드백유지보수 어려움소프트웨어 신뢰 저하 테스트를 통해서 얻을 수 있는 것빠른 피드백자동화안정테스트 코드를 작성하지 않는다면?변화가 생기는 매순간마다 발생할 수 있는 모든 Case를 고려변화가 생기는 매순간마다 모든 팀원이 동일한 고민빠르게 변화하는 소프트웨어의 안정성을 보장할 수 없다.테스트 코드가 병목이 된다면?프로덕션 코드의 안정성을 제공하기 힘들다.테스트 코드 자체가 유지보수하기 어려운, 새로운 짐이 된다.잘못된 검증이 이루어질 가능성이 생긴다.[섹션 3] 수동 테스트 vs 자동화된 테스트수동 테스트 : 콘솔에 출력하며, 사람이 직접 확인한다.단점사람이 검증해야 한다.다른 사람이 봤을 때, 어떤 것을 검증해야 하는지 알기 어렵다. 자동 테스트 : 자동화 테스트 도구로 검증을 자동으로 한다.장점사람이 직접 검증하지 않는다.테스트 환경과 검증 목표를 뚜렷히 알 수 있다.JUnit5로 테스트하기단위 테스트 : 작은 코드 단위(클래스 or 메서드)를 독립적으로 검증하는 테스트검증 속도가 빠르고, 안정적이다.테스트 케이스 세분화하기해피 케이스, 예외 케이스 -> 경계값 테스트(범위, 구간, 날짜 등)정상 작동만 테스트하는 것이 아니라, 예외 상황도 검증을 해야 한다!테스트하기 어려운 영역을 분리하기현재 시간, 날짜 , 랜덤 값 등 고정적인 것이 아닌 유동적인 요소를 분리하자테스트하기 어려운 영역을 외부로 분리할수록 테스트 가능한 코드는 많아진다.테스트하기 어려운 영역?관측할 때마다 다른 값에 의존하는 코드현재 날짜/ 시간, 랜덤 값, 전역 변수/함수, 사용자 입력 등외부 세계에 영향을주는 코드 표준 출력, 메시지 발송, 데이터베이스에 기록 순수 함수같은 입력에는 항상 같은 결과외부 세상과 단절된 형태테스트하기 쉬운 코드[섹션 4]TDD : Test Driven Development프로덕션 코드보다 테스트 코드를 먼저 작성하여 테스트가 구현 과정을 주도하도록 하는 방법레드 - 그린 - 리팩토링레드 : 실패하는 테스트 작성 (구현부 없이 테스트 작성)그린 : 테스트 통과를 위한 최소한의 코딩 (최소한의 구현)리팩토링 : 구현 코드 개선 및 테스트 통과 유지선 기능 구현, 후 테스트 작성테스트 자체의 누락 가능성특정 테스트 케이스만 검증할 가능성 -> 해피 케이스잘못된 구현을 다소 늦게 발견할 가능성선 테스트 작성, 후 기능 구현복잡도가 낮은 테스트 가능한 코드로 구현할 수 있게 한다.쉽게 발견하기 어려운 엣지 케이스를 놓치지 않게 해준다.구현에 대한 빠른 피드백을 받을 수 있다.과감한 리팩토링이 가능해진다.TDD : 관점의 변화테스트는 구현부 검증을 위한 보조 수단 -> 테스트와 사옿 작용하며 발전하는 구현클라이언트 관점에서의 피드백을 주는 Test Driven(테스트 주도)[섹션 5]테스트는 []다테스트는 문서다.프로덕션 기능을 설명하는 테스트 코드 문서다양한 테스트 케이스를 통해 프로덕션 코드를 이해하는 시각과 관점을 보완어느 한 사람이 과거에 경험했던 고민의 결과물을 팀 차원으로 승격시켜서, 모두의 자산으로 공유할 수 있다.DisplayName을 섬세하게테스트 코드는 팀 전체가 보는 것모두가 쉽고, 명확하게 알아볼 수 있게끔 적어야 한다.테스트 목적과 환경, 검증 요소를 명확하게테스트 행위에 대한 결과까지 기술하기도메인 용어를 사용하여 한층 추상화된 내용을 담기메서드 자체의 관점보다 도메인 정책 관점으로테스트의 현상을 중점으로 기술하지 말 것특정 시간 이전에 주문을 생성하면 실패한다 -> 영업 시작 시간 이전에는 주문을 생성할 수 없다. 명사의 나열보다 문장으로 작성A이면 B이다.A이면 B가 아니고, C다.BDD 스타일로 작성하기BDD, Behavior Driven DevelopmentTDD에서 파생된 개발 방법함수 단위의 테스트에 집중하기보다, 시나리오에 기반한 테스트케이스(TC) 자체에 집중하여 테스트한다.개발자가 아닌 사람이 봐도 이해할 수 있을 정도의 추상화 수준(레벨)을 권장Given / When / ThenGiven : 시나리오 진행에 필요한 모든 준비 과정 (객체, 값, 상태 등)When : 시나리오 행동 진행Then : 시나리오 진행에 대한 결과 명시, 검증어떤 환경에서(Given) 어떤 행동을 진행했을 때(When) 어떤 상태 변화가 일어난다(Then)DisplayName에 명확하게 작성할 수 있다.[Day7 미션]https://github.com/taeyeongKims/readable-code/tree/day7[미션 회고]실제로 배웠던 것을 제대로 적용하지 못해서 아쉽다.추상에만 신경을 쓰느라, 일급 컬렉션, 상수화 등의 디테일을 신경쓰지 못했다.또한, 단일 책임 원칙에서도 시간권 패스에 라커 패스가 함께 묶여 있는 게 맞는 걸까?라는 생각도 들었다.리스코프 치환 원칙을 생각하느라 공통 인터페이스를 만들었지만 치환성에 중점을 둔 나머지, 다른 원칙을 어긋낸 건 아닐까라는 생각도 든다.또한, 의존성 역전 원칙을 어떻게 적용할지는 감도 안 잡혔었다.하지만, 섹션 7의 리팩토링 강의 영상으로 보니, 이런 식으로 연결짓는구나 라고 조금은 감이 잡히게 되었다.리팩토링에는 정답이 없다고는 하지만, 반복 연습하다보면 정답에 가까워지지 않을까,,? 라는 생각이 든다.앞으로도 계속 클린코드에 대해 생각해보며 코드를 작성해야 겠다.   [2주차 회고]리팩토링 강의가 끝나고, 테스트 강의로 넘어가는 주차였다.리팩토링 미션을 진행하며 아쉬움이 많은 주차였으며강의 1개를 완주하였다는 뿌듯함도 남은 주차였으며테스트 강의를 시작하는 기대감이 생긴 주차였다.지금까지 많은 강의를 보았지만, 깊게 생각해본 강의는 이번이 처음이었던 거 같다.앞으로 많은 코드를 만들고, 보겠지만 이번 주차 강의가 많이 생각날 듯하다. 

cleancode

kamser

Readable-code 메타인지를 하다.

📚 Readable-code 학습 회고 2주차목차1. 강의 수강일주일간 강의 요약 정리일주일간 강의 회고다음 주에 학습목표 2. 미션미션을 해결하는 과정어떤 관점에서 접근했는지문제를 해결하는 과정왜 그런 식으로 해결했는지미션 해결에 대한 간단한 회고  강의 수강강의 요약추상은 하나의 문단을 한 문장으로 정리하기 위해 이 단락의 주제를 나타내는 것.우리는 추상화 레벨을 높여 가독성을 높이고 중요한 내용을 표현합니다.구체적인 구현 코드를 내부로 숨기기 때문에 사용자에게 불필요한 정보를 최소한으로 노출하게 해야합니다.잘못된 추상화는 오히려 사용자의 가독성을 떨어뜨리고, 내부 기능을 확인하는 과정이 발생합니다.객체지향은 내부 상태나 기능을 숨기고 중요한 추상 인터페이스만 노출하기 때문입니다.사용자 관점에서 내부 로직을 알지 못해도 메서드 시그니처를 통해 충분히 알 수 있도록 해야합니다.그리고 객체지향 코드가 항상 정답은 아닙니다.객체지향 프로그래밍은 요구사항으로 변경되는 환경에 기존 코드를 변경하지 않고 확장할 수 있다는 장점이 있습니다.소프트웨어에 요구사항이 없는 경우는 거의 없지만, 항상 객체지향이 정답이 아니라는 것을 말해주고 계십니다.객체의 책임과 응집도를 묶기 위해서 사용자의 요구사항이 변경 될 경우 변경되는 지점을 하나로 묶는 것도 방법입니다.화면에 출력하고, 사용자에 입력을 코드로 변환하는 과정은 input, output 으로 다른 역할이라고 할 수 있습니다.사용자의 입력을 받기 위해 콘솔에 출력하는 문자열과 사용자의 입력을 파싱하는 input 클래스 모두 같이 수정됩니다.이 두 객체 각각은 역할이 다르지만 하나의 책임(사용자의 스터디카페 이용권 선택받기)으로 묶으면 응집도도 올라가게 됩니다.관점의 차이가 신기했습니다.구체적인 동작 방식을 추상화를 하니 인터페이스도 동작 방식이 메서드 명에 녹아있게 되었습니다. 이용권 파일 읽기 기능을 추상화하여 이용권 읽기가 되니 다른 이용권이 추가되면 인터페이스에 매서드 추가가 되고 처음 만들어진 이용권 파일 읽기 클래스 메서드에 추가됩니다. 스터디 카페 이용권 읽기 클래스이기 때문입니다. 계속 이용권에 대한 변경이나 추가 사항으로 확장되면 클래스는 점점 메서드가 추가되고 인터페이스 메서드도 추가됩니다. 구현 방식을 추상화하니 이렇게 인터페이스도 구현체에 결합이 생긴 기분이 듭니다.구현체를 통해 추상화하는 방법에 구현 방식이 포함되지 않도록 하는 것이 방법일 수 있습니다. 이용권 읽기 인터페이스가 아니라 좌석 정보 제공 인터페이스, 사물함 정보 제공 인터페이스를 추가하니 내부에서 어떤 구현 방식을 사용하더라도 똑같이 결과를 받아올 수 있게 되었습니다.능동적으로 읽기코드를 읽을 때 단순히 눈으로 읽는 것이 아니라 문맥에 따라 개행이나 주석, 메서드 추출로 메서드를 분리하여 도메인 정보를 정확하게 얻는 것이 중요하다고 합니다.이 부분은 저도 정말 많이 공감하고 있습니다.메타인지제 머리 속에 아하 모먼트가 동작하게 된 키워드 입니다.메타인지 자기가 아는 영역인지, 존재 유무만 아는지, 아예 어떤지 감도 안오는 경우로 나눌 수 있습니다.아는 영역과 설명할 수 있는 영역은 100%를 만족하기 어려우므로 적절한 비율을 맞추면 좋다고 합니다.강의 회고제가 그동안 리팩토링을 하면서 고민하던 내용을 깔끔하게 정리하게 된 주간입니다.객체지향 프로그래밍에서 어디까지 객체로 묶어야하는 지 고민이 많았습니다.예를 들어, inputHandler, outputHandler 두 클래스를 하나로 뭉쳐야할지 고민했습니다.하나의 관심사를 가지고 있다는 건 생각을 했습니다.어차피 inputHandler를 수정하면 outputHandler도 수정해야한다.수정사항이 생기는 부분을 같이 묶고 내부 상태를 같이 사용하면 응집도도 높아지고 테스트도 하기 편해질거같다.생각만 했습니다.inputHandler은 이용권 정보 파일(scv)을 읽는 책임이 있기 때문에 다른 클래스의 필드로 넣는게 맞는지 확신이 서지 않았습니다.이제는 관심사에 대해서 어느정도 동일한 변경사항으로 수정해야한다면 같은 오브젝트에서 관리하는게 맞다는것을 알게되었습니다.관점의 차이에서도 머리가 띵! 했습니다.구체적인 방식을 추상화하다보니 인터페이스도 역할이 아니라 동작 방식에 대한 추상화가 되어있었습니다.그러면 인터페이스를 의존하는 다른 클래스는 인터페이스만 보고도 어떤 파일을 읽어오는지 알게됩니다.DIP에 따르면 상위 모듈은 하위 모듈을 의존하면 안되고, 상위 모듈 과 하위 모듈은 모두 인터페이스를 의존해야합니다.구체적인 방식이 인터페이스 표현되는 것을 표현하기 위한 거라면 괜찮지만그게 아니라 추상적으로 표현하려고 한거 였다면 수정해야합니다.함께 자라기 - 탑 다운 방식에 대한 회고저는 개발자로 일을 하면서 제가 해야하는 프로젝트는 요구사항이 명확한게 좋았습니다.회사에서 필요한 기능을 만드는데 어떤 기능이 필요한지 정확하게 모른다는 게 저로서는 이해가 안되었습니다.제가 받은 프로젝트를 개발하는 시간보다 오히려 기획자와 커뮤니케이션 하는 과정이 더 길때도 많아지다보니기획자가 중요하게 생각하는 프로젝트가 아닌건가 ? 라는 생각도 종종 들기도 했습니다.최근 신규 프로젝트가 기획자가 아닌 영업팀에서 필요하다는 기능을 요구했고단 글자수 200 글자 내외로 간단하게 요구를 했습니다.기획에는 어떻게 사용하는지, 무슨 목적인지, 언제 사용하는지, 누가 사용하는지 내용이 없고 단순 기능만 만들어달라는 기획이였습니다.처음에는 이런 생각이였습니다.그냥.. 만들어달라는 기능만 만들자그런데 누가 쓰고, 어떻게 쓰는지는 알아야할 거같아서 회의에 참석한 팀장님들에게 돌아다니면서 물어봤습니다.개발 팀장님, 기획 팀장님, 임원분에게 물어보면서 어떻게 기획이 되었고, 누가 사용하게 되는지 물어보고 나서프로세스를 구체적으로 만들었다가 다시 추상적으로 표현하면서 보이지 않았던 신규 프로젝트의 구조가 보이기 시작했습니다.프로젝트를 다시 들여다보니 아무 생각없이 개발하던 것보다 프로세스가 단순해졌고, 기존 프로젝트에도 적용한다면 유지보수와 사용자들도 편하도록 전체 구조가 바뀌게 되었습니다.강사님이 말씀하신게 이런게 아닌가 싶습니다.탑다운 방식으로 주어진 선택지 내에서 해결하는 방법은 빠르게 개발을 시작할 수 있으나추상과 구체를 오가면서 깨닫는게 있다는게 이런게 아닌가 싶었습니다.드나드는 것이 생각보다 저에게 많은 인사이트를 주었습니다.다음 학습 목표테스트 코드를 사용한다는 의미가 무엇인지 알고 싶습니다.테스트 코드가 불필요한 짐이 되지 않기 위해서는 어떻게 관리해야하는지 공부하고 싶습니다.추가로 기술을 배우고 기존과 달라진게 있다면 바로 정리하는 습관을 길들이겠습니다.미션미션을 해결하는 과정어떤 관점에서 접근 했는지저는 미션 코드의 큰 그림을 스프링 MVC 패턴과 톰캣 & 스프링 컨테이너 이라고 생각하며 접근을 시도했습니다.톰캣은 StudyCafeApplication 이며 사용자의 요청과 응답을 줄 수 있는 환경을 제공한다.스프링 컨테이너는 StudyCafePassMachine으로 보고 사용자의 요청과 응답을 서비스 계층에 변환하여 전달하는 역할이다.내부 구현 코드는 Service 계층이기에 외부 입력과 출력에 영향을 받지 않도록 해야한다.문제를 해결하는 과정은 무엇이었는지패스입력을 변환 하기 전까지는 컨트롤러 계층이라고 생각했습니다. 요청 파라미터를 변환하여 서비스 계층이 이해할 수 있는 언어로 변환하여 전달한다.서비스 계층은 외부가 어떠한 방식으로 요청을 받든지 상관없이 자신이 기능을 제공하기 위해 필요한 파라미터를 추상화하여 표현한다이 두 관점을 가지고 객체 관심사로 역할을 나누려고 했습니다.문제가 입력과 출력이 이미 역할이 부여된 것이라고 생각을 하다보니 객체를 합치기 어려웠고 리팩토링 하는 과정에 추상화로 묶기 어려웠습니다.그래서 아쉽게도 목적만 들어내기 위한 메서드 추출이 되었고 코드는 복잡해지기만 했습니다.왜 그런 식으로 해결했는지 내용스프링은 객체지향 프로그래밍을 하기 위한 다양한 기술을 제공하는 프레임 워크입니다.사용자가 원하는 이용권 조회 및 이용권 금액 계산, 할인 정책은 핵심 비즈니스 로직인 service 계층으로 바라본다.사용자의 입력과 출력은 HTTP 프로토콜에서 문자열을 필요한 정보와 서비스 계층에서 필요한 양식으로 변환하는 컨트롤러 구조파일에서 읽던, 메모리에서 읽던, DB에서 읽던 결과는 서비스 계층이 읽을 수 있는 데이터 구조로 반환하는 리포지토리 구조이렇게 바라보면서 코드를 작성하면 스프링 MVC 구조로 객체지향적이면서 유지보수하기 좋은 코드로 작성될 수 있다고 생각했습니다.미션 해결에 대한 간단한 회고미션해결과 중간정검을 통해 배운게 있습니다.직접 해보자.익숙해질때까지 하자.지금까지 학습 방식은 강의를 보고 끄덕끄덕, 그리고 인프런에서 보여주는 100% 게이지를 보며 만족했습니다.실제 예제 코드를 리팩토링하려니 뇌 메모리에 그동안 배운 정보가 로드되었습니다.코드로 출력하려고 하니 2시간째 코드만 바라보고 있었습니다.뇌 속에 정보는 많은데 익숙하지 않으니 키보드로 출력이 되지 않았습니다.강사님이 말씀하신 메타인지 중 들어본적이 있는 영역까지만 도달한거 같습니다.미션 해결을 하면서 강의만 듣고 이해한 것은 제가 학습한게 아니라는 것을 느꼈습니다.중간점검 피드백미션 Day4 코드를 자신의 것만 보는게 아니라 다른 사람의 것도 보는 것이 중요하다고 하셔서 비교해보니많이 배울것이 있었습니다. 짧은 20줄도 안되는 코드를 비교해보며 개선점을 찾아가기 위함입니다.미션 Day-4요구사항✔ 사용자가 생성한 '주문'이 유효한지를 검증하는 메서드. ✔ Order는 주문 객체이고, 필요하다면 Order에 추가적인 메서드를 만들어도 된다. (Order 내부의 구현을 구체적으로 할 필요는 없다.) ✔ 필요하다면 메서드를 추출할 수 있다.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 boolean validateOrder(Order order) { ​ if (isEmpty(order)) { outputHandler.showMessage("주문정보가 없습니다."); return false; } ​ if(order.doesNotHaveItems()) { outputHandler.showMessage("주문 항목이 없습니다."); return false; } ​ if (order.doseNotHaveValidTotalPrice()) { outputHandler.showMessage("올바르지 않은 총 가격입니다."); return false; } ​ if (order.doesNotHaveCustomerInfo()) { outputHandler.showMessag("사용자 정보가 없습니다."); return false; } return true; } ​ private boolean isEmpty(Object object) { return object == null; }전xx 개발자님public boolean validateOrder(Order order) {    try{         if (order.hasNoOrderItems()) {             throw new OrderException("주문 항목이 없습니다.");         } ​         if (order.hasInValidTotalPrice()) {             throw new OrderException("올바르지 않은 총 가격입니다.");       } ​        if (order.hasNoCustomerInfo()) {             throw new OrderException("사용자 정보가 없습니다.");       }   } catch (OrderException e){        log.info(e.getMessage());        return false;   } catch (Exception e){        e.printStackTrace();        return false;   } ​    return true; }배울점전xx 개발자님이 인프런에서 작성하신 코드를 보면서 배운점을 정리했습니다.코드를 머리속에 있는 것을 먼저 정리하셨습니다.코드 이해를 하고 코드리팩토링에 대한 명확한 근거를 남겨주셨습니다.코드 이해 부분객체의 Item이 존재하지 않는다면, return false1의 로직이 아니면서 총 가격이 0보다 크지 않다면, return false1의 로직이 아니면서 총 가격이 0보다 크면서 2-1. 사용자 정보가 없다면, return false 2-2. 2-1의 로직이 아니라면, return true그 외, return true리팩토링 부분if (order.getTotalPrice() > 0) // 2번 else if (!(order.getTotalPrice() > 0)) // 3번2번 조건과 3번조건은 논리적 부정으로 if-else 구조와 동일합니다.유효하지 않은 조건을 먼저 적용해도 상관없는 순서입니다.인지적 사고와 불필요한 작업공간을 남기지 않도록 순서를 변경합니다public boolean validateOrder(Order order) {    if (order.getItems().size() == 0) {        log.info("주문 항목이 없습니다.");        return false;   }   if (order.getTotalPrice() <= 0) {       log.info("올바르지 않은 총 가격입니다.");     return false;   }   if (!order.hasCustomerInfo()) {       log.info("사용자 정보가 없습니다.");       return false;   }   return true; }배울점리팩토링을 통해 어느 개선점이 생겼는지 설명할 수 있어야한다.저도 동일한 조건문 순서를 변경했지만 설명까지 하지는 못했습니다.이 부분이 강사님이 말씀하신 능동적으로 리팩토링하기가 아닌가 싶습니다.코드를 먼저 이해할 때 주석이나 별도로 메모에 작성한다.그리고 코드 이해한것을 간단하게 작성하고, 코드 순서를 변경하여 early return을 사용했습니다.if (order.getTotalPrice() > 0) // 2번 else if (!(order.getTotalPrice() > 0)) // 3번2번과 3번은 논리적 부정으로 if-else구조로 변경할 수 있다.if(!(order.getTotalPrice() > 0)) else if-else 구조로 변경이 되면 early return으로 코드를 단순화하기 편하다.이렇게 정리해서 작성하니 명확하게 리팩토링한 이유가 생기게 되었습니다.리팩토링한 이유를 작성하면서 연습한다.공백으로 의미 단위를 나눠보자.공백 단위로 의미를 나누어 사고의 흐름을 원활하게 한다.메서드 추출 이유불필요한 부정 연산자 제거하여 두가지 조건을 생각하지 않도록 한다.추상화 레벨을 동등하게 하여 사고의 흐름을 원활하게 한다.이렇게 리팩토링하는 이유를 작성하면서 연습을 하는 것이 강사님이 알려주신 내용을 더 오래 기억할 수 있는거 같습니다.해피케이스와 예외케이스사전에 처리되지 않은 예외를 잡아서 개발자가 확인할 수 있도록 한다.public boolean validateOrder(Order order) {    try{         if (order.hasNoOrderItems()) {             throw new OrderException("주문 항목이 없습니다.");         } ​         if (order.hasInValidTotalPrice()) {             throw new OrderException("올바르지 않은 총 가격입니다.");       } ​        if (order.hasNoCustomerInfo()) {             throw new OrderException("사용자 정보가 없습니다.");       }   } catch (OrderException e){        log.info(e.getMessage());        return false;   } catch (Exception e){        e.printStackTrace();        return false;   } ​    return true; }제가 코드를 작성하면서 생각하지 못한 부분이였습니다.그러면서 예외로 처리한다면 아래 항목도 고려해봐야겠다는 생각이 들었습니다.정상 처리로 변경하지 말아야하는 경우라면지금 validateOrder 메서드는 order 객체에서 발생되는 예외를 모두 잡고 정상 흐름으로 돌립니다.정상 흐름으로 넘어가도 되는 예외정상 흐름으로 넘어가면 안되는 예외오브젝트 메서드가 예외를 던지지 않는다면 try-catch가 필요할까이렇게 세분화해서 예외 처리를 하는 건 어떨까 생각이 들었습니다.개발자가 생각한 범주 내에 예외는 정상흐름으로 변경한다.개발자가 생각하지 못한 범주는 정상흐름으로 동작하지 않도록 하여 호출자에게 현재 상태를 알린다.이렇게 예상하지 못한 예외를 외부로 던져 호출자가 예외에 대한 처리를 할 수 있도록 유연하게 설계할 수 있다. 단 체크 예외는 제외하고개발자가 예측하지 못한 예외를 검증 메서드 내에서 처리하는 것은 검증 메서드의 책임이 커지는거같다.수정 코드public boolean validateOrder(Order order) {  try {    if (isOrderEmpty(order)) {      return false;   }    if (order.hasNoOrderItems()) {      return false;   } ​    if (order.hasInValidTotalPrice()) {      return false;   } ​    if (order.hasNoCustomerInfo()) {      return false;   }    return true; } catch (IllegalArgumentException e) {    log.info(e.getMessage());    return false; } catch (Exception e) {    log.error("알 수 없는 오류가 발생했습니다. {} {} {}", e.getMessage(), e.getCause(), order);    throw new RunTimeException(e); } } ​ private boolean isOrderEmpty(Order order) {  return order == null; }이렇게 리팩토링을 하니 추가로 고려할만한 사항이 생겼습니다.예외 발생 가능성을 점검해보자order 엔티티가 발생시키는 예외가 진짜 있는지 확인한다. 만약 해당 메서드가 단순히 boolean만 반환한다면 try-catch를 제거해도 됩니다.예외 오브젝트를 추상화해보자Exception은 어떤 예외인지 알 수 없는 최상위 추상 예외이므로 검증하다가 발생했다는 걸 표현하는게 좋을거같습니다.최종 수정public boolean validateOrder(Order order) {    try {        if (order == null) {            return false;       }        if (order.hasNoOrderItems()) {            return false;       }        if (order.hasInValidTotalPrice()) {            return false;       }        if (order.hasNoCustomerInfo()) {            return false;       }        return true;   } catch (IllegalArgumentException e) {        log.warn("잘못된 입력값: {}", e.getMessage());        return false;   } catch (Exception e) {        log.error("알 수 없는 오류 발생 - 주문 ID: {}, 원인: {}",                  order != null ? order.getId() : "없음", e.getMessage(), e);        throw new OrderValidationException(e);   } }코드 출처인프런 워밍업 클럽 3기 백엔드(클린코드, 테스트코드) Day4 미션출처인프런 워밍업 클럽인프런 워밍업 클럽 스터디 3기 - 백엔드 클린코드, 테스트 코드(Java, Spring Boot)강의 링크Readable Code: 읽기 좋은 코드를 작성하는 사고법Practical Testing: 실용적인 테스트 가이드

김진환

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

 1. 재귀함수에서 기저조건을 만들지 않거나 잘못 설정했을 때 어떤 문제가 발생할 수 있나요?무한 loof에 빠져 해당 프로세스가 종료되지 않거나, 콜스택이 가득 차서 프로세스가 강제 종료 될 때 까지 해당 루틴이 계속 실행된다. 2. 0부터 입력 n까지 홀수의 합을 더하는 재귀 함수를 만들어보세요.function sumOdd(n){ if(n<0) { return 0}; if(n%2==0){ return 0+sumOdd(n-1); } return n+sumOdd(n-2); } console.log(sumOdd(10)) // 25 3. 다음 코드는 매개변수로 주어진 파일 경로(.는 현재 디렉토리)에 있는 하위 모든 파일과 디렉토리를 출력하는 코드입니다. 다음 코드를 재귀 함수를 이용하는 코드로 변경해보세요. const fs = require("fs"); // 파일을 이용하는 모듈 const path = require("path"); // 폴더와 파일의 경로를 지정해주는 모듈 function traverseDirectory1(directory){ const stack = [directory]; // 순회해야 할 디렉토리를 저장할 스택 if(stack.length==0){ return; } const currentDir = stack.pop(); const files = fs.readdirSync(currentDir); for (const file of files) { const filePath = path.join(currentDir, file); const fileStatus= fs.statSync(filePath); if (fileStatus.isDirectory()) { // 해당 파일이 디렉토리라면 traverseDirectory1(currentDir); } else { console.log('파일:', filePath); } } } traverseDirectory1("."); // 현재 경로의 모든 하위 경로의 파일, 디렉토리 출력 

김진환

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

 1. FIFO 스케줄링의 장단점이 뭔가요?- 장점 : 구현이 간단하고 직관적이다.- 단점 : 먼저 온 프로세스가 작업시간이 긴 프로세스인 경우 나중에 도착한 작업시간이 빠른 프로세스가 기다려야 한다. 또한 작업시간이 긴 프로세스에 I/O 작업이 있을 경우 CPU가 대기 상태로 들어가 자원 활용룰이 떨어진다. 2. SJF를 사용하기 여러운 이유가 뭔가요?짧은 작업 우선 스케줄링은 작업이 빨리 끝나는 순서대로 처리하는 스케줄링이지만 도착한 프로세스가 언제 작업을 마칠지 알 수 없어 구현이 어어렵다.또한 작업이 빠른 프로세스가 지속해서 들어오게 될 경우 상대적으로 일찍 도착하였지만 작업시간이 긴 프로세스가 무한정 기다릴 수 있다. 3. RR 스케줄링에서 타임 슬라이스가 아주 작으면 어떤 문제가 발생할까요?라운드 로빈에서 타임 슬라이스가 매우 짧다면 프로세스가 무언가를 처리해야하는 시간보다 컨텍스트 스위칭 오버헤드가 더 크게 작동하게 된다. 4. 운영체제가 MLFQ에서 CPU Bound Process와 I/O Bound Process를 어떻게 구분할까요?해당 프로세스가 CPU를 얼만큼 사용하는지를 체크한다. OS가 지정한 타임 슬라이스 만큼 프로세스를 사용한다면 이는 CPU위주의 작업임을 알 수 있고, OS가 지정한 타임 슬라이스 이전에 CPU 자원을 반환한다면 이는 타임 슬라이스 이외에 다른 작업인 I/O 작업등을 통해 반납한 것이므로 OS는 해당 프로세스가 CPU위주의 작업인지 I/O등의 작업 위주 프로세스인지 알 수 있다. 5. 공유자원이란무엇인가요?프로세스간 통신시에 공동으로 사용하는 파일이나 변수.여러 프로세스가 동시에 접근할 수 있는 자원이다. 6. 교착상태에 빠질 수 있는 조건은 어떤 것들을 충족해야할까요?어떤 프로세스가 점유하고 있는 리소스가 다른프로세스에게 공유될때어떤 프로세스가 점유하고 있는 리소스를 다른 프로세스가 빼앗을 때어떤 프로세스가 어떤 리소스를 갖고 있는 상태에서 다른 리소스를 원하는 상태일때점유와 대기를 하는 프로세스들의 관계가 상호 순환(원형)관계를 이루고 있을때. 

서희원

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

재귀함수에서 기저조건을 만들지 않거나 잘못 설정했을 때 어떤 문제가 발생할 수 있나요?재귀함수가 계속 본인을 호출하면서 콜 스택이 가득 차게 되어 StackOverFlow가 발생할 수 있다. 0부터 입력 n까지 홀수의 합을 더하는 재귀 함수를 만들어보세요.function sumOdd(n){ if(n === 0) { return 0; } if(n < 3) { return 1; } // 재귀 로직 let isOdd = n%2 === 1; if(isOdd) { // 홀수면 return n + sumOdd(n-2); // -2씩 하면서 더하기 } else { // 짝수면 return sumOdd(n-1); // 이전 홀수로 가서 계산 } } console.log(sumOdd(10)) // 25 console.log(sumOdd(1)) // 1 console.log(sumOdd(2)) // 1 console.log(sumOdd(3)) // 4 console.log(sumOdd(4)) // 4  다음 코드는 매개변수로 주어진 파일 경로(.는 현재 디렉토리)에 있는 하위 모든 파일과 디렉토리를 출력하는 코드입니다. 다음 코드를 재귀 함수를 이용하는 코드로 변경해보세요.function traverseDirectory2(directory){ if(directory) { console.log('디렉토리: ', directory); const files = fs.readdirSync(directory); for( const file of files){ const filePath = path.join(directory, file); const fileStatus= fs.statSync(filePath); if(fileStatus.isFile()){ console.log('파일: ', filePath); } else { traverseDirectory2(filePath); } } } } traverseDirectory2("..");

인프런워밍업클럽스터디미션

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

2주차 회고 차주 계획 및 다짐 다음주부터 리팩토링 프로젝트를 시작한다.일단 소스 코드를 보고 이해해야 하는 상황인데, 테스트 코드를 중심으로 살펴보면 좋을 것 같다.테스트 코드에 녹여진 내용을 보고 의도와 기능을 파악해 볼 예정이다.만약 의도가 잘 나타나지 않는다? 리팩토링 포인트라고 생각하고 해당 부분을 개선해보자.읽기 좋은 코드 강의에서 보았던 역할과 책임 등등의 이론도 적용해보는 좋은 실습 대상으로 생각해보자. 일찍 일어나고, 집중력 있는 하루를 보내자.지금 이 순간이 좋은 추억으로 남으려면, 후회하지 않으려면 열심히 해야 한다.긍정적으로 생각하고, 다른 사람들을 포용할 수 있는 심적 여유를 가지는 일주일이 됐으면 한다.할 수 있다. 힘내자. 웃자.  정리 가장 빠른 길테스트 코드 작성을 소홀히했다.기간 내에 기능을 구현하기 위해 테스트 코드는 제쳐두고 주먹구구식으로 개발하기에 급급했으나,결국 문제점이 발견되어 다시 돌아와 예외처리를 하고 기능을 수정하는 일이빈번했다.결국 테스트 케이스를 제대로 작성하는 것이 가장 빠른 길이다.테스트를 통해 버그를 발견할 수 있고, 기능 변경에 따른 영향도 파악이 가능하며,테스트의 의도를 분명히 함으로써 팀원들 간 생각을 공유할 수 있는 매개체가 된다.가까이 보면 느리지만, 멀리 보면 가장 빠르다.테스트는 귀찮지만 해야한다. 잊지말자. 테스트 케이스 세분화해피 케이스와 예외 케이스 모두 작성해야 한다.특히 경계값이 있는 조건에 대한 테스트 작성이 중요하다. 테스트하기 어려운 영역을 분리하기테스트하기 어려운 영역, 제약조건이 있어 테스트 진행이 어려운 경우, 테스트를 수행할 때 마다 값이 변경되는 경우.외부에서 원하는 값을 주입 가능하도록 변경한다.만약, 테스트가 어려운 메서드라고 생각되는 경우 메서드 설계를 변경해보자.기능 주도로 개발하다보면 위와 같은 상황을 자주 마주할 수 있다. 테스트 주도 개발 방식실패하는 테스트 작성, 초록불을 보기 위한 최소한의 코딩, 리팩토링내가 작성한 코드에 대해 자주, 빠르게 피드백을 받을 수 있음.선 기능 구현, 후 테스트 작성의 단점테스트 자체를 누락(귀찮고 테스트하기 힘들어~), 특정 해피 케이스만 작성, 잘못된 구현 등등..위와 같은 단점을 극복할 수 있다.테스트를 어떻게 할지 고민함으로써 복잡도가 낮은 유연한 코드를 작성할 수 있게 되고,즉시 피드백을 받을 수 있다.테스트 주도 개발 방식을 통해 어떤 식으로 효율적으로 테스트할 수 있을지, 테스트 설계 단계부터 고민해보자.TDD가 만능은 아니지만? 이를 판단하기 위해선 TDD를 극한까지 사용해보아야 한다. 테스트는 문서다.테스트 코드는 프로덕션 기능을 설명하는 가장 좋은 문서다.개발에서의 고민점들을 테스트 코드에 녹여놓았기에 어떤 고민의 결과물을 팀 간 공유할 수 있다.특정 기능의 역할을 파악하기도 쉬울 듯 하다.디스플레이 이름을 섬세하게 작성하자.~테스트 라고 작성하지 말고..문장 형태로 행위와 결과를 기술하기. BDDgiven, when ,then 으로 분리, 시나리오에 기반한 테스트케이스 작성. 

백엔드

최준

인프런 워밍업 클럽 백엔드-code 2주차 발자국

학습 요약 & 회고읽기 좋은 코드와 객체 지향이전에는 읽기 좋은 코드와 객체 지향과 관련이 없다고 생각했다. 객체 지향은 그냥 프로그래밍의 하나의 방법이자 개념이라고 생각했다. 늘 코드를 작성하기 전에 객체가 어떤 것이 있는지 식별하고 어떤 관계를 가지고 이런 것에 집중했던 것 같다.  하지만 이번 강의를 들으며 놀라운 경험을 했다. 객체 지향적으로 짤 수록 더 읽기 좋은 코드가 되는 것을 느꼈다. SOLID 원칙도 왜 중요한지 깨닫게 되었다. SOLID 원칙을 지키지 않고 여러 책임이 혼재된 코드는 당연히 읽기 어려울 수 밖에 없다.  그리고 이전에 이펙티브 자바 책을 읽으며 '아 이런 것이 자바로 코딩할 때 좋은 점들이고 기술이구나' 이정도로 생각했다. 왜 상속보다 조합이 좋은지, 일급 컬렉션을 사용해야 하는지 잘 모르고 그냥 유지보수하기 좋으니까, 상속은 결합도가 높으니까 이렇게 받아드렸다. 그렇다 보니 이펙티브 자바를 이해할 때 어려움이 있었고 잘 기억에도 남지 않았던 것 같다.  이번 강의를 들으면서 이펙티브 자바에 나왔던 개념들을 적용하며 읽기 좋은 코드가 되는 것을 보았다. 이렇게 객체지향의 개념을 적용하니 코드가 읽기 더 수월해짐을 느꼈다. 그러면서 다시 이펙티브 자바를 읽어보고 어떻게 적용해 읽기 좋은 코드를 작성할 수 있을지 고민해봐야 겠다.  미션Day 7 미션 : 스터디 카페 키오스크 프로그램을 읽기 좋은 코드로 바꾸는 미션 우선 section 2, 3에서 배운 간단한 리팩토링을 진행하려고 했다. if-else문을 피하고 빠른 return을 하려고 했다. 또한 공통적인 코드들이 보여 메서드로 추출하려고 했다.  if문으로 패스 종류에 따라 분기되던 것들을 메서드로 추출했다. 하지만 마지막 locker 관련된 부분이 달라 어려움을 겪었다. 어찌저찌 메서드로 패스 종류 받도록 추출하기는 했지만 뭔가 억지스러운 것 같다.  하지만 아직 객체지향을 적용해 읽기 좋은 코드로 만들기는 익숙하지 않은 것 같다. 리팩토링한 코드가 여전히 여러 책임을 가지고 관심사가 모여 있는 느낌이다. 하지만 어떻게 분리를 해야 할지, 어떻게 객체지향적으로 메시지를 주고 받게 할지 잘 보이지 않는 것 같다.  지뢰 찾기를 다시 강의를 보며 리팩토링 해보고 한번 더 새롭게 리팩토링에 도전해 봐야 겠다.

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

이 글은 박우빈님의 readable-code강의 를 참조하여 작성한 글입니다.정신없이 진도표를 따라 강의를 수강하고 미션을 진행하였더니 일주일이 순식간에 지나가버렸다.처음에는 할 만하다고 느꼈었는데,,,강의 내용을 습득하여 스스로의 힘으로 코드에 적용까지 하려니 우빈님이 당부하신 대로 쉽지 않았던 나날들이었던 것 같다,,🥹특히 이번 스터디카페 리팩토링 미션에 뻥 안치고 10시간 이상은 투자한 것 같은데,,,ㅋㅋㅋㅋ(미션 당일 + 자고 읽어나서 맑은 정신으로 한 번 더 도전)이후에 리팩토링 강의를 수강하며 내가 전혀 고려하지 못했던 부분을 리팩토링하신 것을 보고 놀랐다. 뿐만 아니라, 리팩토링을 뚝딱 해내시는 모습에서도 감탄했다.원래 누군가가 뭔가를 쉽게 해내는 것처럼 보이면 진짜 고수라는 말이 딱 맞는 것 같다...!강의 속에서 리팩토링 하시는 모습은 굉장히 쉬워보였는데...혼자서 해내려니 정말 막막했다 ㅋㅋㅋㅋ그래도 나도 경험을 더 쌓고 나면, 지금보다 더 짧은 시간 안에 더 객체지향적으로 리팩토링을 해낼 수 있겠지!!이를 위해 다른 분들이 리팩토링 하신 코드도 많이 읽으면서 여러 번 리팩토링 해봐야겠다. 학습 내용 요약 주석의 양면성주석의사 결정의 히스토리 를 도저히 코드로 표현할 수 없을 때, 주석으로 상세하게 설명하자주석이 많다 == 비지니스 요구사항을 코드에 잘못 녹였다 는 의미가 성립됨주석을 작성할 때, 자주 변하는 정보는 최대한 지양해서 작성하자 (그렇지 않으면, 주석도 신경써서 계속 업데이트 해줘야 하는 단점 존재)변수와 메서드의 나열 순서상태 변경 > 불리언 등 판별 >= 조회 메서드 순으로 메서드 순서 나열 패키지 나누기패키지: 문맥으로써의 정보를 제공할 수 있음대규모 패키지 변경은 팀원과의 합의를 이룬 후 하자본인만 사용하는 부분이면 괜찮지만, 여러 사람과 공통으로 사용하는 클래스들의 패키지를 한번에 변경하면 충돌이 발생할 수 있음! 은탄환은 없다클린코드가 은탄환은 아니다 (클린코드가 무조건적인 정답은 아니다)요구사항이 변경될 일이 없는 코드는 절차지향적인 코드가 오히려 정답일 수 있음 (e.g., 체스는 500년동안 규칙이 변하지 않았음)실무는 지속가능한 소프트웨어의 품질 vs 기술부채를 안고 가는 빠른 결과물 사이의 줄다리기무조건적으로 클린 코드를 추구하기보다는 주어진 기간을 최대한 맞추게끔 결과물을 내놓고, 이후 미래에 잘 고치도록 할 수 있는 코드 센스가 필요 (e.g., 주석으로 리팩토링 할 부분 남겨 놓기 등) 미션 Day 4 미션 피드백기존 코드 (return 타입 - boolean)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; } 내가 리팩토링 한 코드 중 일부 (1)public class Order { private long id; private List<Item> items; private Customer customer; public Order(final List<Item> items, final Customer customer) { validateOrder(items, customer); this.items = items; this.customer = customer; } private void validateOrder(final List<Item> items, final Customer customer) { if (doesNotHaveItems(items)) { throw new RuntimeException("주문 항목이 없습니다."); } if (doesNotHaveCustomerInfo(customer)) { throw new RuntimeException("사용자 정보가 없습니다."); } } public boolean doesNotHaveItems(final List<Item> items) { return items.isEmpty(); } public boolean doesNotHaveCustomerInfo(final Customer customer) { return customer == null; } } => boolean을 return하고 있는 기존 메서드에 대한 리팩토링으로 예외를 던지는 것으로 변경하는 것은 좋을 수도, 나쁠 수도 있다.(자칫하면 오버엔지니어링이 될 수 있음)📌 예외를 던지는 것은 비싸기 때문에, 신중하게 메서드의 사용현황을 파악 후 상황에 맞게 리팩토링 할 것!! 내가 리팩토링 한 코드 중 일부 (2)public class Item { private static final int MINIMUM_VALUE = 1; private long id; private String name; private int price; public Item(final long id, final String name, final int price) { validatePositivePrice(price); this.id = id; this.name = name; this.price = price; } private void validatePositivePrice(final int price) { if (price < MINIMUM_VALUE) { throw new RuntimeException("올바르지 않은 가격입니다."); } } }고민이었던 부분: Order 클래스 내에 Item 클래스를 새로 만들어, 이를 리스트 형태로 Order에 의존성 주입하도록 코드 리팩토링을 진행하였다. 이 때 Item 객체를 생성할 때 마다 금액이 양수인지 검증하고 있는데, Order 클래스에서 전체 총 금액이 양수인지 검증을 다시 한번 해주는 게 좋을지 고민이 됐었다.=>  우빈님 답변: 이미Item 객체를 생성할 때 금액의 유효성을 보장하고 있으니, Order 클래스는 굳이 하지 않아도 될 것 같다.물론 비지니스 로직이 복잡해지면, 오히려 필요하다고 느끼는 순간이 올 수 있으니, 그 때 추가해도 늦지 않다! Day 7 미션미션 한 줄 소개: 스터디 카페 이용권 선택 시스템 리팩토링 하기 리팩토링 한 부분 코드 중복 제거 및 메서드 추출StudyCafePassMachine의 의존성 config에서 주입: StudyCafePassMachine은 필요한 의존성을 외부에서 주입받기만 하고, 내부에서 어떻게 사용하는지는 외부에 노출하지 않을 수 있다!일급 컬렉션 활용: 일급 컬렉션으로 분리함으로써, 원래는 private 메서드라 테스트하지 못했던 로직도 테스트 가능해짐!무분별한 getter 사용이 아닌, 객체에 메세지 보내기public boolean isSamePassTypeWith(final StudyCafePassType studyCafePassType) { return passType == studyCafePassType; }passType를 비교해야 하는 곳에서 passType를 게터를 통해 비교해주는 게 아닌, isSamePassTypeWith 메서드와 같이 객체에 메세지를 전달하자!스터디 카페 이용권 인터페이스 적용: 이부분은 리팩토링을 잘 하였는지 감이 안온다..시도에 의의를 두자 ㅎ헤ㅔㅎpublic interface StudyCafePassHandler { boolean isAppliable(final StudyCafePassType studyCafePassType); StudyCafePasses findCandidateStudyCafePasses(final StudyCafePasses studyCafePasses); } ========= # StudyCafePassMachine private void processUserSelection(final StudyCafePassType studyCafePassType) { final StudyCafePasses availablePasses = getAvailablePasses(studyCafePassType); outputHandler.showPassListForSelection(availablePasses); final StudyCafePass selectedPass = inputHandler.getSelectPass(availablePasses); if (studyCafePassType == StudyCafePassType.FIXED) { checkLockerPass(selectedPass); return; } outputHandler.showPassOrderSummary(selectedPass, null); }Hourly, Weekly, Fixed라는 3종류의 카페 이용권이 존재하기 때문에, 인터페이스를 정의하여 if문 사용을 자제하고, 상황에 맞는 이용권을 가져왔다. 리팩토링 놓친 부분FileHandler: 데이터를 어디로부터 어떻게 가져올 것인가에만 초점이 맞춰져 있음-> File관련 로직이 들어나면 FileHandler 가 방대해질 것개선 방향: provider를 통해 어떤 데이터를 필요로 하는가에 초점을 맞출 것SeatPassProviderLockerPassProvider domain영역에 view관련 로직 침투public enum StudyCafePassType { HOURLY("1", "시간 단위 이용권"), WEEKLY("2", "주 단위 이용권"), FIXED("3", "1인 고정석"); private final String command; private final String description; StudyCafePassType(final String command, final String description) { this.command = command; this.description = description; } public static StudyCafePassType from(final String userInput) { return Arrays.stream(StudyCafePassType.values()) .filter(studyCafePassType -> studyCafePassType.command.equals(userInput)) .findAny() .orElseThrow(() -> new AppException("잘못된 입력입니다.")); } } PassType은 중요한 도메인 모델인데, Input과 관련된 의미를 지닌 command가 침투되었다. passType을 선택하는 command가 변경된다면, 단순히 입력 방식을 바꿨을 뿐인데 도메인 모델을 수정해야 하는 좋지 않은 상황이 발생한다.StudyCafePassOrder 도메인 추출스터디 카페 좌석 이용권 + 사물함 이용권을 합친 Order 도메인을 새로 추출할 수 있다.이로 인해 FIxedPassType에만 적용되는 사물함 로직 분기문을 간단하게 처리할 수 있었다!StudyCafePass 내 LOCKER_TYPES 상수 선언public enum StudyCafePassType { HOURLY("시간 단위 이용권"), WEEKLY("주 단위 이용권"), FIXED("1인 고정석"); private static final Set<StudyCafePassType> LOCKER_TYPES = Set.of(FIXED); private final String description; StudyCafePassType(String description) { this.description = description; } public boolean isLockerType() { return LOCKER_TYPES.contains(this); } public boolean isNotLockerType() { return !isLockerType(); } }전혀 생각지도 못했지만, 알아두면 참 좋은 객체에 메세지를 보내는 방법에 대해 배웠다.LOCKER_TYPES를 StudyCafePassType enum내에 적용하여 처리할 수 있다니도메인 지식이 부족해서 그랬나, 나는 생각지도 못했던 방법이다.나는 상위 도메인에서 if문을 통해 매번 확인해주었는데, 우빈님이 하신 방법이 더 책임 분리가 잘되어있고 객체에 메세지를 보내는 좋은 방법인 것 같다!!또한 Locker type이 늘어나도, set에 내용만 추가해주면 된다는 점에서 유지보수도 훨씬 쉬울 것 같다! 마무리강의를 굉장히 빠른 시간 안에 완강했다!!!그렇지만 강의 내용을 완전히 내 것으로 만들었다기엔 부족하다,,,,강의 볼 때는,,'이럴 때 조합, 일급 컬렉션, VO 등을 적용 하는구나~!' 를 배우면서, 앞으로 스스로 잘 판단해 낼 줄 알았는데,,,혼자서 해 보려니까 너무나도 막막했다.그래도 강의 내에서 주신 미션을 통해 내가 어느 부분이 부족한지 파악할 수 있었던 것 같다. 무엇보다도 너무 재밌었다,,,시간 가는 줄 모르고 했던 것 같다.이제 강의는 끝이 났지만 지뢰찾기랑 스터디카페 코드에 대해 복습할 것이다. 리팩토링 적용하기 어려웠던 부분을 반복 작성해 보면서 체득시킬 생각이다. 이렇게 반복하다보면 우빈님의 사고법을 체득할 수 있겠지요...?다음 주부터는 테스트 코드에 대해 배우는데,,평소 테스트 코드에 대해 공부를 많이 하지 않았어서 새로 배우는 양이 어마어마 할 것 같다.강의 내용을 잘 습득할 수 있도록 메타인지 열심히 해야겠다!나 자신 아자아자 화이팅이다💪🏻 [출처]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

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

강동훈

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

FIFO 스케줄링의 장단점이 뭔가요?FIFO(First In First Out) 스케줄링이란 대기 큐에 들어온 작업 순서대로 실행하는 스케줄링을 의미한다. 비선점형 스케줄링으로, 하나의 프로세스가 CPU에 할당받아 작업이 시작되면 해당 프로세스의 작업이 마무리되기 전까지 중단되지 않는다.✅ 장점 스케줄링 구현이 단순하다들어온 작업 순서대로 모든 작업이 실행되기 때문에 기아(Starvation)현상에 빠지지 않는다.컨텍스트 스위칭이 자주 발생하지 않아,오버헤드가 적다✅ 단점들어온 작업 순서에 따라, 작업의 시간이 긴 프로세스가 먼저 실행될 경우 작업의 시간이 짧은 프로세스들은 오래 대기해야 하며 응답시간 또한 길어진다. (Convoy Effect, 호위효과)I/O작업을 대기하는 동안 CPU는 다른 작업으로 전환할 수 없기 때문에(비선점형), CPU 사용률이 저하된다.✅ 평균대기시간case 1: 프로세스_1 (Burst Time: 25초) - 대기 시간: 0초프로세스_2 (5초) - 대기 시간: 25초프로세스_3 (4초) - 대기 시간: 30초평균 대기 시간: 55 / 3 = 18.3초case2:프로세스_3 (4초) - 대기 시간: 0초프로세스_2 (5초) - 대기 시간: 4초프로세스_1 (Burst Time: 25초) - 대기 시간: 9초평균 대기 시간: 13 / 3 = 4.3초프로세스의 실행 순서에 따라 평균대기시간의 편차가 커지기 때문에 현대 운영체제에서는 잘 사용하지 않는다.먼저 들어온 작업 순서대로 처리를 해주는 작업 혹은 성능의 저하없이 모든 작업을 순서대로 처리하는 일괄 처리 시스템에서 효율적이다. SJF를 사용하기 여러운 이유가 뭔가요?SJF (Shortest Job First) 스케줄링은 Burst Time이 가장 짧은 작업부터 우선 실행하는 스케줄링이다. FIFO의 단점이었던 '실행 순서에 따른 평균 대기시간 편차'를 극복하기 위해 설계되어 실행 시간이 짧은 프로세스 먼저 작업하는 기법이다. 하지만 두 가지 큰 단점이 존재한다. 첫 번째는 Starvation(기아) 현상이 발생할 수 있다. 짧은 작업을 우선적으로 처리하기 때문에, 긴 작업의 프로세스는 계속 대기를 하게 되어 실행의 순서가 보장되지 않는다. 두 번째는 실제 Burst Time을 예측하기 힘들다는 것이다. 짧은 실행 순서를 먼저 실행시키는 SJF 스케줄링에서, 실제 작업이 얼마나 CPU를 사용할 것인지 혹은 I/O 작업이 언제 응답 받을 수 있는지 예측될 수 없기 때문에 실 Burst Time을 예측하여 짧은 프로세스 먼저 실행시키기 어렵다. SJF는 결론적으로 실행 시간이 예측 가능하며 짧은 작업이 많은 환경에서 평균 대기 시간을 효율적으로 줄일 수 있는 스케줄링 기법이다. RR 스케줄링에서 타임 슬라이스가 아주 작으면 어떤 문제가 발생할까요?RR(Round Robin) 스케줄링이란 타임 슬라이스만큼 프로세스를 실행하고 다음 프로세스를 실행하는 스케줄링이며 타임 슬라이스는 CPU가 할당되는 일정한 시간을 의미한다.타임 슬라이스가 아주 작게 실행되면 여러 프로세스가 동일한 시간에 실행되는 것처럼 보일 수 있지만 짧은 시간을 주기로 Context Switching이 많아져 오버헤드가 증가한다. 타임 슬라이스가 너무 크게 실행되면 FIFO 스케줄링과 유사하게 동작하여 효율성이 떨어지게 된다. 즉, RR 스케줄링은 최적의 타임 슬라이스를 설정하는 것이 중요하며 최적의 타임 슬라이스는 여러 프로세스가 버벅이지 않으면서 동시에 실행시키는 효과를 줌과 동시에 오버헤드가 너무 크지 않은 정도의 할당 시간을 의미한다. 윈도우에서는 20ms, 유닉스에선 100ms정도로 설정된다. 운영체제가 MLFQ에서 CPU Bound Process와 I/O Bound Process를 어떻게 구분할까요?MLFQ(Multi Level Feedback Queue)란 프로세스를 여러 개의 우선순위 큐로 분류하고, 실행 시간에 따라 큐 간 이동이 가능한 스케줄링 알고리즘이다. 처음 프로세스가 실행되면 높은 우선순위의 큐에서 실행되며, 실행되는 시간에 따라 우선순위가 변동되어 점차 다른 큐에 할당된다. 주어진 타임 슬라이스 내에 작업이 완료된다면 I/O Bound Process로 간주하여 높은 우선순위를 유지하고,해당 타임 슬라이스를 초과하면 CPU Bound Process로 간주되어 낮은 우선순위 큐로 이동하게 된다. 실행 순서는 우선순위가 높은 큐부터 순차적으로 실행되며, 낮은 우선순위의 큐는 오래 동안 실행되지 않는 상태인 기아(Starvation) 현상을 겪을 수도 있기 때문에 Aging을 통해 문제를 해결할 수 있다. Aging은 낮은 우선순위 큐에서 오랫동안 기다린 프로세스들을 점진적으로 다시 우선순위를 높여주는 기법이다.  I/O Bound Process가 우선순위가 높은 이유처음에는 I/O 작업은 요청을 한 뒤에 응답까지 기다려야 하니, 작업 시간이 예측하기 힘들고 오래걸리지 않나? 라고 생각하였고타임 슬라이스 안에 작업이 보통 완료하지 못하지 않을까라는 생각에 우선순위가 높은 이유가 궁금하여 다시 해당 부분을 찾아보았다. I/O 작업은 CPU를 거의 사용하지 않고 보통 외부 장치를 통해 응답을 기다리는 작업들이다. CPU의 점유를 가로채지 못하는 비선점형 스케줄링 기법에서는 I/O작업이 실행되면 응답까지 기다려야 하니(대표적으로 FIFO 같은 기법) 작업이 빠르다고 말할 수는 없지만, 선점형 스케줄링 기법에서는 I/O 작업이 요청되면 해당 프로세스는 대기 상태로 상태값이 변경되고 응답이 오기 전까지(인터럽트) 다른 프로세스의 작업을 실행할 수 있기 때문에 작업이 빠르다고 얘기할 수 있으며 우선순위 또한 높게 가져갈 수 있는 것이다. 공유자원이란무엇인가요?공유자원이란 여러 프로세스 간 공유되는 자원(변수나 파일 등)을 의미한다. 여러 프로세스는 서로 통신을 하며 한 가지 작업을 동시에 수행할 수도 있는데, 이 과정에서 공유자원을 여러 프로세스가 접근하게 되면 경쟁 조건이 발생하면서 동기화 문제가 생긴다. 경쟁 조건이란 다수의 프로세스(혹은 쓰레드)가 같은 데이터(공유자원)에 동시에 접근 혹은 조작의 순서에 따라 결과의 일관성을 해칠 수 있는 상태를 의미하며 이를 해결하기 위해서는 상호 배제가 필요히다. 상호배제는 하나의 프로세스만 접근할 수 있는 구역인 임계구역을 설정하여 데이터의 동기화를 유지시키는 것을 의미한다. 상호배제 요구사항임계영역엔 하나의 프로세스만 접근 가능하다여러 요청에도 하나의 프로세스만 접근을 허용한다임계구역에 들어간 프로세스는 최대한 빠르게 나와야 한다. 상호배제의 매커니즘Mutex임계 구역에 진입하면 lock을 획득하고 acquire()와 release()를 통해 획득 및 반납프로세스가 임계구역에 진입하기 위해 무한 루프가 돌며 이를 spinlock이라고도 부른다Semaphore공유자원 수용 가능 수에 따라 정수의 변수 s를 선언wait(s) signal(s) 를 통해 s값을 증가 차감한다함수 호출 순서를 잘못하면 문제가 발생한다.Monitor세마포어의 단점을 해결한 상호배제 메커니즘운영체제의 차원이 아닌 프로그래밍 언어에서 처리한다자바에서 synchronized 붙은 함수가 실행되면 다른 프로세스가 접근 할 수 없음함수를 임계 영역을 감싸지 않아도 되어 편리함. 교착상태에 빠질 수 있는 조건은 어떤 것들을 충족해야할까요?교착상태(Deadlock)란 여러 프로세스가 서로 다른 프로세스의 작업이 끝나기를 기다리다가 아무도 작업을 진행하지 못하는 상태를 의미한다. 교착상테 필요조건상호배제: 한 프로세스가 리소스를 점유하였다면 다른 프로세스 접근 불가비선점: 점유중인 프로세스의 리소스를 뺏어갈 수 없음점유와 대기: 한 프로세스는 리소스를 점유하고 있는 상태에서 다른 프로세스의 리소스 점유를 대기해야 함원형대기: 점유와 대기를 하는 프로세스들의 관계가 원형이어야 함.데드락의 발생은 막을 수 없으니, 발생 후의 해결 방법을 고민해본다. 1. 가벼운 교착상태 검출: 타이머 이용. 프로세스가 일정 시간동안 작업을 진행하지 않는다면 교착상태로 검출 - 일정시간마다 체크포인트 생성, 교착상태 예상 시에 롤백2. 무거운 교착상태 검출: '자원 할당 그래프'를 통해 프로세스에 할당된 자원을 모니터링 - 교착상태를 일으킨 프로세스 강제종료 - 체크포인트로 롤백 자원할당 그래프(Resource Allocation Graph)란 무엇일까?운영체제에서 자원이 할당되어 있는 상태를 그래프로 그려, 시각적으로 데드락의 여부를 판단- T = {T1, T2, ..., Tn}: 실행 중인 쓰레드- R = {R1, R2, ..., Rm}: 할당될 자원 타입- T_i → R_j: i 쓰레드가 j 자원을 요청한다- R_j → T_i: j 자원을 i 쓰레드에 할당되어 있다.위 예제에서는 3개의 쓰레드와 4개의 자원이 있으며 2개의 순환 구조를 갖고 있다.T1 → R1 → T2 → R3 → T3 → R2 → T1T2 → R3 → T3 → R2 → T2모든 쓰레드는 하나의 자원을 점유 중인 상태에서 다음 자원을 점유하기 위해 대기 상태에 있고, 이러한 상태가 원형을 이루어지고 있기 때문에 데드락에 빠질 확률이 높다고 판단한다. 📔 회고미션을 수행하면서도 강의를 들었을 때 고민하지 못했던 부분에 대해서 더 찾아보고 전체적인 흐름을 다시 정리해볼 수 있었다.MLFQ 스케줄링에서 I/O Boud Process를 정리하면서 우선순위가 높은 이유에 대해서 생각해보진 못했던 것 같은데,선점형과 비선점형의 특징을 알고, 선점형에서 I/O 작업이 응답 대기를 통해 다른 프로세스로 전환될 경우 CPU 작업 처리 비중은 적으니 빠른 작업 실행과 높은 우선순위를 가져갈 수 있다라는 것을 다시 한 번 전체적인 흐름과 다른 개념과의 연관을 통해 정리해볼 수 있었다.또한 질문의 답변을 통해서 강의를 듣고 추가로 공부해봤던 것들에 대해서 한 번 더 정리해볼 수 있는 시간을 가졌는데, 뮤텍스와 같은 상호배제 메커니즘이나 처음에 이해하지 못했던 자원 할당 그래프를 직접 다시 확인해보고 개념들을 이해해볼 수 있었던 것 같다.   

시스템 · 운영체제운영체제인프런워밍업미션

서희원

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

FIFO 스케줄링의 장단점이 뭔가요?First In First Out먼저 들어온 프로세스를 먼저 처리한다는 것인데, 먼저 들어온 프로세스가 아주 길 경우에 뒤에 들어온 프로세스가 정말 빨리 끝나는 프로세스 임에도 불구하고 한참 기다려야 하는 단점이 있다.반면에 장점은 단순하고 직관적이라는 장점이 있다. SJF를 사용하기 여러운 이유가 뭔가요?Shortest Job First가장 짧은 프로세스를 먼저 실행한다는 건데 가장 짧은 프로세스를 예측하기가 힘들다.짧은 프로세스들이 계속 치고 들어오면 긴 프로세스는 영영 실행되지 못하고 계속 기다려야 하는 상황이 발생해서 사용하기가 어렵다.  RR 스케줄링에서 타임 슬라이스가 아주 작으면 어떤 문제가 발생할까요?Round Robin타임 슬라이스가 아주 작으면 컨텍스트 스위칭이 빈번하게 일어나 오버헤드가 많이 발생하게 되어서 속도가 오히려 느려질 수 있다. 운영체제가 MLFQ에서 CPU Bound Process와 I/O Bound Process를 어떻게 구분할까요?Multi Level Feedback Queue배정 받은 타임 슬라이스를 모두 사용하는지를 관찰한다.CPU Bound: 대부분 타임 슬라이스를 모두 사용한다.I/O Bound: 타임 슬라이스가 끝나기 전에 I/O를 요청한다. 공유자원이란무엇인가요?여러 프로세스들이 공유하는 자원을 말한다. 교착상태에 빠질 수 있는 조건은 어떤 것들을 충족해야할까요?상호배제자원은 한 프로세스에만 할당됨비선점먼저 차지한 자원을 다른 프로세스가 강제로 빼앗을 수 없음점유와 대기이미 사용 중인 자원을 사용하려고 프로세스가 대기함원형 대기점유와 대기를 하는 프로세스들이 원형을 이룸 

인프런워밍업클럽스터디미션

서희원

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

운영체제프로세스 동기화프로세스 간 통신한 컴퓨터 내에서 프로세스 간에 통신은 어떻게 할까?운영체제가 만든 파이프를 통해 통신한 프로세스 안에서 쓰레드 간 통신네트워크를 이용한 통신소켓 통신 (운영체제가 제공)RPC 통신 (다른 컴퓨터에 있는 함수를 호출 - 원격 프로시저 호출)공유자원과 임계구역공유자원여러 프로세스가 공유하는 변수나 파일여러 프로세스가 공유하기 때문에 순서에 따라서 값이 다를 수 있음컨텍스트 스위칭으로 실행되기 때문에 누가 먼저 실행되는지 알수가 없거나 어렵다따라서 연산 결과를 예측하기 어렵다여기서 발생하는 문제가 동기화 문제임계구역여러 프로세스가 동시에 사용하면 안되는 구역이 문제를 해결하려면 상호 배제 매커니즘이 필요 상호 배제 매커니즘임계구역에는 동시에 하나의 프로세스만 접근한다동시에 여러 요청이 와도 하나의 프로세스만 접근한다임계구역에 들어간 프로세스는 최대한 빠르게 나와야 한다 세마포어열쇠 관리자가 열쇠를 가지고 있고 열쇠를 한 명한테만 준다프린터: 공유자원프린터를 사용하려는 지원들: 프로세스프로세스들이 기다리는 공간: 대기 큐열쇠 관리자: 운영체제열쇠: 세마포어 실제로 세마포어는 정수형 변수로 여러 개의 열쇠를 가질 수 있다.예를 들어 공유자원이 2개라면 열쇠도 2개다.단, 열쇠 제공과 반납을 이상하게 호출해서 잘못 사용할 수도 있다는 단점이 있다. // 임계 영역 진입 전 semaphore.wait(); try { // 임계 영역 코드 } finally { // 임계 영역 퇴출 시 semaphore.signal(); }이런식으로 항상 wait()과 signal() 은 쌍으로 호출되어야 하는데 연속으로 각각의 것을 호출하면 안된다.연속으로 wait()를 호출한 경우 > 데드락 발생연속으로 signal()를 호출한 경우 > 자원 낭비 모니터세마포어의 단점을 해결한 상호배제 매커니즘운영체제가 아닌 프로그래밍 언어에서 자체 지원한다. 명시적으로 wait()과 signal() 를 호출하지 않아도 자동으로 됨Java에서는 synchronized 키워드를 통해 모니터를 구현한다.public class BoundedBuffer { private final int[] buffer = new int[5]; private int count = 0; private int in = 0; private int out = 0; // synchronized 키워드로 상호배제 보장 public synchronized void produce(int item) throws InterruptedException { // 버퍼가 가득 찼다면 대기 while (count == buffer.length) { wait(); // 다른 스레드가 notify할 때까지 대기 } buffer[in] = item; in = (in + 1) % buffer.length; count++; notify(); // 대기 중인 소비자에게 알림 } public synchronized int consume() throws InterruptedException { // 버퍼가 비었다면 대기 while (count == 0) { wait(); // 다른 스레드가 notify할 때까지 대기 } int item = buffer[out]; out = (out + 1) % buffer.length; count--; notify(); // 대기 중인 생산자에게 알림 return item; } } 데드락공유자원을 서로 차지하려다가 발생하는 교착 상태식사하는 철학자 문제5명의 철학자가 원형 테이블에 앉아있고 각 철학자 사이에 포크가 1개씩 있다철학자는 양쪽의 포크 (2개)가 필요하다포크는 한 번에 한 사람만 사용할 수 있다.모든 철학자가 동시에 왼쪽 포크를 잡으면 > 오른쪽 포크를 집으려고 모두가 대기 > 아무도 식사를 시작할 수 없는 교착상태가 발생 교착상태의 필요조건상호배제: 먼저 리소스를 차지했다면 다른 프로세스가 사용할 수 없음비선점: 한번 차지한 리소스는 다른 프로세스가 빼앗을 수 없음점유와 대기: 리소스를 가진 상태에서 다른 리소스를 기다림원형 대기: 점유와 대기를 하는 프로세스들이 원형을 이룬다이 중 한가지라도 충족되지 않으면 교착상태가 발생하지 않는다 해결하는 법교착상태 회피 어느 정도 할당해야 교착상태가 발생하지 않는지 파악해서 할당전체 자원의 수, 할당된 자원의 수로 안정상태와 불안정 상태를 나눔불안정 상태에 있더라도 무조건 교착상태인 것은 아니지만 교착상태가 될 확률이 높다 교착상태 검출가벼운 교착상태 검출프로세삭 일정시간 동안 진행되지 않으면 교착상태라고 간주일정 시점마다 체크포인트를 만들어서 저장하고 타임아웃이 나면 롤백무거운 교착상태 검출자원 할당 그래프를 이용운영체제가 어떤 프로세스를 쓰는지 지켜보다가 문제가 발생하면 해결계속 지켜봐야 하기 때문에 오버헤드가 발생함억울하게 종료되는 프로세스는 발생하지 않음  컴파일코드를 기계어로 바꿔서 실행파일로 만드는 것컴파일을 할 때 타입, 문법을 검사한다속도가 빠르다C, C++, C#코드 > 전처리기 > 컴파일러 > 어셈블러 > 링커 > 실행파일 완성! 인터프리터컴파일 과정이 없고 코드를 한 줄 씩 해석해서 실행하는 것미리 검사를 하지 않기 때문에 에러가 발생할 수 있음속도가 느리다JavaScript, Python, Ruby  메모리레지스터휘발성 메모리32bit, 64bit == 레지스터의 크기CPU는 레지스터에 있는 값을 가져와서 메인 메모리에 저장한다CPU가 사용하는 메모리로 엄청나게 빠르다캐시휘발성 메모리RAM이 너무 느려서 미리 데이터를 가져다 둘 곳이 필요해서 생김가장 속도가 빠른 순서로 나열하면 L1 캐시 > L2 캐시 > 메인메모리메인메모리 (RAM)휘발성 메모리가장 중요한 메모리로 실제 운영체제 및 다른 프로세스들이 올라가는 곳가격이 매우 비싸서 데이터를 저장하는 게 아닌 실행 중인 프로그램만 올라간다보조저장장치 (HDD, SSD) 비휘발성 메모리게임, 파일 등을 저장할 때 사용한다왜 다른데에 저장 안하냐? > 너무 비싸서 다른애들은 가성비가 좋지 않음메모리와 주소RAM을 관리하려고 운영체제가 메모리를 1바이트씩 나누고 구역에 이름을 매김 == 주소논리주소프로세스가 바라보는 메모리 주소0번지로 시작하는 연속된 메모리 공간으로 보임 (가상)각 프로세스마다 독립된 주소 공간을 가짐물리주소실제 RAM의 하드웨어 주소모든 프로세스가 공유하는 실제 메모리 공간절대주소고정된 메모리 위치재배치가 불가상대주소재배치가 가능한 상대적인 주소경계 레지스터레지스터 공간에서 운영체제가 있는 공간(커널 공간)이랑 사용자 공간을 나누었는데이 경계를 지키는게 경계 레지스터메모리 접근 과정프로세스의 논리주소 > 재배치 레지스터로 주소 변환 > 물리 메모리에 접근 메모리 할당 방식유니 프로그래밍 방식[시스템 메모리 구조] 물리 메모리(RAM) ┌─────────────┐ │ Process A │ ├─────────────┤ │ Process B │ ↔️ [스왑 영역(디스크)] ├─────────────┤ ┌─────────────┐ │ Process C │ │ Process D │ └─────────────┘ └─────────────┘메모리 오버레이 > 큰 프로그램을 메모리에 올릴 때 일부만 메모리에 올리고 일부는 하드디스크에 저장했음메모리에 올릴 때는 하드디스크 내부 스왑 영역에 올림스왑 과정이 있기 때문에 실제 메로리가 큰 컴퓨터 보다는 느리게 동작한다 멀티 프로그래밍 방식가변 분할 방식 (Segmentation)프로세스의 크기에 따라 메모리를 나누는 방법 > 연속된 메모리를 할당연속된 공간에 딱 맞게 할당 되어서 내부 단편화가 없음대신 외부 단편화가 발생 - 외부 단편화란?메모리의 빈 공간들이 있지만 프로세스의 크기보다 각각 작아서 할당되지 못하는 문제해결하려면 메모리 조각 모음을 하면 되는데 조각 모음을 할 때는 실행 중인 프로세스들을 일시 중지 시켜야 해서 오버헤드가 발생한다- 내부 단편화란?프로세스의 크기보다 큰 메모리에 프로세스가 할당되어서 낭비 공간이 발생하는 것 고정 분할 방식 (Paging)프로세스의 크기와 상관없이 메모리를 할당 > 한 프로세스가 메모리에 분산되어서 할당됨 > 비연속 할당구현이 간단하고 오버헤드가 적음작은 프로세스도 큰 메모리에 할당 되기 때문에 낭비공간이 생김 > 내부 단편화 현상 있음 버디 시스템가변 분할 방식과 고정 분할 방식을 혼합해서 단점을 최소화 한 방식2의 승수로 메모리를 분할해서 메모리를 할당각 분할된 영역은 "버디(짝)"를 가지며, 인접한 두 버디가 모두 가용 상태면 합칠 수 있다.가변 분할 방식처럼 프로세스 크기에 따라 메모리의 크기가 달라지면서 외부 단편화를 방지하기 위해서 메모리를 확보하는 것이 간단왜 간단할까?원래는 메모리 빈 공간 크기가 제각각이어서 이동 위치 계산이나 주소 재배치가 복잡했음버디 시스템에서는 조각들이 모두 2의 승수 크기이기 때문에 버디끼리만 합치면 된다 내부 단편화가 발생하긴 하지만 그 공간이 매우 작음  자료구조와 알고리즘재귀자기 자신을 참조하는 것으로 꼭 종료 조건(기저 조건)이 있어야 한다.콜스택 First In Last out함수를 호출하면 콜스택에 올라가고 종료되는 콜스택에서 제거됨isStackOverflowError 는 기저 조건 없이 실행되어 콜 스택이 꽉 차서 오류난 것재귀적으로 생각하기재귀함수를 쉽게 작성하려면 재귀함수가 이미 구현되어있다고 생각하고 하위 문제의 결과를 기반으로 현재 문제의 계산을 구성한다. > 하향식 계산 (가장 큰 문제에서 시작하여 작은 문제로 나누어 해결하는 방식)return number * factorial(number - 1); // 이 부분은 이미 구현되어있다고 생각한다상향식 계산과 하향식 계산function factorial2(number, i = 1, sum = 1) { if(i > number) return sum; return factorial2(number, i + 1, sum * 1) } // 상향식 계산 (재귀)재귀를 이용한다고 해서 모두 하향식 계산인 것은 아니다.이렇게 재귀를 사용해서도 상향식 계산을 할 수 있지만 하향식은 재귀로만 가능함상향식 재귀는 성능이 좋지 않으므로 하향식으로 하는게 좋다. 정렬버블정렬구현이 쉽지만 성능이 그렇게 좋지 않음두 개를 비교해서 큰 애를 뒤로 보내는 방식한 번 순회가 끝나면 마지막 원소는 이미 끝났으니 제외해야 한다구현function BubbleSort(arr) { for(let i = 0; i < arr.length - 1; i++) { for(let j = 0; j < (arr.length - i - 1); j++) { if(arr[j] > arr[j+1]) { let temp = arr[j]; arr[j] = arr[j+1]; arr[j+1] = temp; } } } }성능바깥 쪽 For문이 돌 때마다 가장 큰 원소가 하나씩 정렬된다.안쪽 For문은 그 갯수가 하나씩 줄어든다.자연수는 버리고 가장 높은 차수를 선택하면 Big(O)는 O(n²)가 된다.대강 For문 2개가 중첩이면 O(n²)라고 생각하면 된다. 선택정렬 이해와 구현이 간단하지만 성능이 떨어짐첫 번째 원소에서 마지막 원소까지 비교 후 가장 작은 값을 첫 번째 원소로 가져온다 구현function selectionSort(arr) { for (let i = 0; i < arr.length - 1; i++) { let minValueIndex = i; // 수정된 부분: j는 i+1부터 시작해야 한다 for (let j = i + 1; j < arr.length; j++) { if(arr[j] < arr[minValueIndex]) { minValueIndex = j; } } // 최솟값을 찾은 후 교환 let temp = arr[i]; arr[i] = arr[minValueIndex]; arr[minValueIndex] = temp; console.log(arr); } }처음에 직접 써봤는데 let j를 i 부터 시작해서 계속 원하는 결과가 나오지 않았음한 번 순회를 돌면 최소값은 찾아진 것이므로 걔를 제외하고 해야한다!성능똑같이 시간 복잡도는 O(n²)  회고👏 칭찬하고 싶은 점일이 많이 바빴는데 밀리지 않고 듣고 정리해 두었다구현을 직접 해보고 오류가 나면 스스로 해결해 보았다 😅 아쉬웠던 점바빠서 운영체제 같은 경우에는 완벽하게 이해를 하지 못했는데 넘어간 부분이 좀 있었다 🔄 보완하고 싶은 점강의를 한 번 듣고 바로 한 번 복기를 하면 이해가 더 잘될 것 같아서 그렇게 해봐야 겠다 🎯 다음주의 목표강의 한 번 듣고 바로 복기하면서 한 번 더 이해해보기들으면서 드는 질문 정리해서 해보기

인프런워밍업클럽스터디발자국

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

고객 설문 / 사용성 테스트 관련 강의 회고1주차 OT때, 고객의 이야기를 들어야 한다고 코치님께서 말씀하셨을 때 VoC를 참고하면 좋겠다는 생각을 했다. 그래서 강의 듣는 중간에 VoC 분석 업무 관련 채용 공고도 찾아보고 했었다. 'PM/PO' 직무가 되는 것을 길게 보고 있는데, 고객을 이해하는 직무를 하면, 제품을 더 잘 이해할 수 있지 않을까 싶었다. 하지만 이게 오히려 독이 될 수 있다는 것을 깨달았다.'사용성 테스트' 개념을 이야기 해주시기 전에는 제품 개발 방법에 대해 잘 몰랐다는 것을 깨달았다. 단순하게 기획 하고 프로토타이핑을 하는 것이 새로운 프로덕트를 만드는 방법 중에 하나라고 생각했다. 하지만 매몰 비용을 줄일 수 있는 방법이기 때문에, 사용성 테스트를 우선 하고 개선할 방법을 찾아가면 된다는 것을 깨달았다.   데이터 분석역량 관련 강의 회고이번주 강의의 핵심은 '고객 설문'인 것 같지만, 데이터 관련 일을 하고 있는 나에게는 '데이터' 관련 강의가 참 인상깊었다. 그리고 데이터에 대해 비 데이터 직군 분들께 어떻게 설명하면 되는지에 대해 고민하게 되는 계기가 되었다.PM/PO 직군에서 요구하는 데이터 분석 역량이 어느 정도인지, 그리고 어느 부분을 공부하면 되는지에 대해 잘 알게 되어서 정말 감사한 챕터였다. (그리고 앞으로 더 감사할 예정이다.) 

wlsgml450

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

자료구조와 알고리즘재귀함수에서 기저조건을 만들지 않거나 잘못 설정했을 때 어떤 문제가 발생할 수 있나요? 기저조건이 없으면 함수가 계속해서 자기 자신을 호출하게 되어 무한 루프에 빠진다. 호출이 계속 쌓이면서 콜스택 메모리 공간이 꽉 차면서 프로그램이 강제 종료될 수 있다.기저조건을 잘못 설정하면 예상과 다른 동작이 발생할 수 있다.그러므로 기저조건을 잘 만들어보자! 0부터 입력 n까지 홀수의 합을 더하는 재귀 함수를 만들어보세요.function sumOdd(n){ // 재귀 로직 if (n <= 0) return 0; if (n % 2 === 0) return sumOdd(n - 1); return n + sumOdd(n - 2); } console.log(sumOdd(10)) // 25 다음 코드는 매개변수로 주어진 파일 경로(.는 현재 디렉토리)에 있는 하위 모든 파일과 디렉토리를 출력하는 코드입니다. 다음 코드를 재귀 함수를 이용하는 코드로 변경해보세요.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); traverseDirectory2(filePath); // 디렉토리면 재귀 호출 } else { console.log('파일:', filePath); } } } traverseDirectory2("."); // 현재 디렉토리 탐색 

알고리즘 · 자료구조

김예원

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

1주 차 학습 내용섹션1. 프로덕트 매니저의 역할, 고유의 전문성프로덕트 매니저의 역할, 고유의 전문성 4가지성공적인 프로덕트의 네가지 조건과 제품 조직이 마주하는 네가지 리스크프로덕트 매니저가 사업 성과를 낸다는 것이란?Pre-PMFPMF 이후 제품 조직이 하는 일 섹션2. 문제, 제대로 정의하고 해결하는 방법문제, PM의 사고 알고리즘 - 문제 정의Problem Space와Solution Space 문제, PM의 사고 알고리즘 - 문제 해결문제 정의는 분석, 문제 해결은 창조의 영역 고객의 모든 문제가 비즈니스가 되진 않는다. 해결할 가치가 있는 문제Problem Worth Solving (해결할 가치가 있는 문제) 1주 차 학습 회고스스로 칭찬하고 싶은 점강의를 두 번씩 반복해서 듣고 노트를 정리하는 등 꼼꼼하게 수강했습니다.아쉬웠던 점 / 보완하고 싶은 점 / 다음 주에는 어떻게 학습할지실무 사례와 더욱 밀접하게 연결 지으려면, 강의를 들을 때마다 “우리 회사 혹은 내가 담당하는 서비스에서 이 개념을 어떻게 적용할 수 있을까?”라는 질문을 상시로 해야겠다고 느꼈습니다.1주 차 미션 해결 과정어떤 관점에서 접근했는지 / 문제를 해결하는 과정은 무엇이었는지 /왜 그런 식으로 해결했는지 / 미션 해결에 대한 회고1주 차 미션은 “프로덕트 매니저의 역할은 무엇일까?”라는 질문이었습니다. 추천해주신 아티클 사이트들(예: SVPG, Product Talk, Shreyas Doshi, Lenny’s Newsletter 등)에서 PM의 역할에 대한 여러 견해를 검색하고 정리한 뒤, 제 생각을 덧붙이는 활동을 했습니다.미션을 진행하면서 다양한 PM 정의를 접했는데, 마치 한 직무 안에 여러 직종이 녹아든 느낌이었습니다. 예를 들어,Marty Cagan은 비즈니스 성과와 고객 가치 모두를 책임지는 ‘결과 중심’을 강조했고,Shreyas Doshi는 팀을 촉진하고 전략적으로 문제를 예방하는 역량,Lenny Rachitsky는 Shape–Ship–Sync라는 프레임워크로 제품 방향 설정부터 실행·조율까지의 전체 과정을 체계화하더군요.이런 견해들을 종합해보니, PM 역할이 단순 기능 기획자가 아니라 사업적 맥락과 사용자 요구를 균형 있게 조정하는 종합 설계자에 가깝다는 걸 느꼈습니다.수강 평강의를 듣기 전에는 PM 업무를 다소 막연하게만 알고 있었는데, 이번 학습과 미션을 통해구체적인 프로덕트 매니저의 역할을 이해하고,어떤 역량을 갖춰야 하는지를 좀 더 명확히 알게 되었습니다.가장 인상 깊었던 부분은 "비즈니스적 관점으로 해결할 가치가 있는 문제를 선택하는 것”이었습니다. UX/UI 디자이너로서는 “사용자가 겪는 문제는 전부 풀어야 한다”고 생각해왔는데, PM의 입장에서는우선순위(비즈니스 임팩트 vs. 사용자 불편도 vs. 기술 리소스)조직 리소스(시간, 예산)시장 기회같은 요소들을 고려해 “이 문제를 풀어야 하는 진짜 이유”를 더 심층적으로 따져봐야 함을 깨달았습니다. 앞으로 남은 학습에서도, “어떻게 하면 문제 정의를 더 명확하게 해 내고, 그 문제를 해결하는 과정에서 팀이 한 목소리를 낼 수 있게끔 할까?”를 중심으로 고민해볼 계획입니다.

기획 · PM· PO김민우인프런워밍업클럽시작하는PM/POPMPO

강동훈

[인프런 워밍업 클럽 3기 - CS] - 2주차 발자국(운영체제)

💻 운영체제📌 프로세스 동기화프로세스는 다른 프로세스와 데이터를 주고 받으며 통신한다.여러 프로세스가 공유자원에 접근하여 데이터를 수정하고 읽을 경우, 동기화 문제가 발생할 수 있다.공유 자원 : 프로세스 간 공유되는 변수나 파일임계 구역: 여러 프로세스가 동시에 사용하면 안되는 구역경쟁 조건: 공유 자원을 서로 사용하기 위해 경쟁하는 것상호배제의 요구사항임계 구역에는 하나의 프로세스만 접근할 수 있다여러 요청에도 하나의 프로세스만 접근 가능하다임계 구역에 들어간 프로세스는 최대한 빠르게 나와야 한다.상호배제 메커니즘세마포어세마포어 변수를 갖고있는 프로세스가 먼저 실행되고 작업이 완료되면 signal()을 통해 변수 반환세마포어 변수가 없는 프로세스는 대기 wait()세마포어 변수를 할당받으면 프로세스 실행 가능공유 자원 수에 따라 세마포어의 수 증가모니터운영체제의 차원이 아닌 프로그래밍 언어에서 처리자바에서 synchronized가 붙은 함수가 실행되면 다른 프로세스가 접근 불가함수를 임계 구역에 감싸지 않아도 되어 편리하게 구현 가능 📌 데드락교착상태(데드락): 여러 프로세스가 서로 다른 프로세스의 작업이 끝나기를 기다리다가 아무도 작업을 진행하지 못하는 상태필요조건1. 상호배제: 한 프로세스가 리소스를 점유하였다면 다른 프로세스 접근 불가2. 비선점: 점유중인 프로세스의 리소스를 뺏어갈 수 없음3. 점유와 대기: 한 프로세스는 리소스를 점유하고 있는 상태에서 다른 프로세스의 리소스 점유를 대기해야 함4. 원형대기: 점유와 대기를 하는 프로세스들의 관계가 원형이어야 함.회피교착상태는 발생할 수 밖에 없다 > 발생의 원인을 줄이거나 빠르게 해결 안정상태 (시스템 총 자원이 14라 가정)현재 총 12개의 작업이 할당되었다.P1이 4개의 자원을 요청하면 사용 가능한 자원이 2(14-12)개 남아있기에 거절P2가 2개의 자원 요청하면 사용 가능한 자원 2개 할당P2의 작업이 마무리되면 사용 가능한 자원 6개로 증가나머지 프로세스들의 요청 예상 자원 커버 가능 불안정상태현재 사용 가능 자원은 1(14-13)개이다.모든 프로세스들의 요청 예상 자원을 할당해줄 수 없다.모든 프로세스들이 최대 자원을 요구하지 않는다면 교착 상태에 빠지지 않을 수 있지만가능성이 높다 가벼운 교착상태 검출 : 타이머 이용. 프로세스가 일정 시간동안 작업을 진행하지 않는다면 교착상태로 검출 일정시간마다 체크포인트 생성, 교착상태 예상 시에 롤백무거운 교착상태 검출: '자원 할당 그래프'를 통해 프로세스에 할당된 자원을 모니터링교착상태를 일으킨 프로세스 강제종료체크포인트로 롤백  📌 메모리1. 레지스터: 가장 빠른 기억 저장소이자 휘발성 메모리. 메인 메모리 데이터를 CPU레지스터를 가져와 연산 후 계산 결과를 다시 메인 메모리에 저장2. 캐시: 메인 메모리에서 필요할 것 같은 데이터를 미리 캐시에 저장3. 메인메모리(RAM): 실제 운영체제와 프로세스들이 저장되는 휘발성 메모리4. 보조저장장치: 비휘발성 메모리이며 프로그램, 파일을 저장 물리 주소(절대 주소): 메모리 공간의 실제 주소논리 주소(상대 주소): 사용자가 다루는 메모리 주소1. 사용자가 프로그램 실행 - 사용자 입장에서는 0x0000 메모리로 작업2. 프로세스는 실제 물리주소 0x4000 주소에 저장3. 사용자가 0x0100 주소의 데이터 요청 (논리 주소)4. 논리 주소(0x0100)와 재배치 레지스터(실행 중인 프로세스의 물리주소 - 0x4000)을 더해서 0x4100의 값을 전달 메모리 할당 방식❓ 유니 프로그래밍 환경에서는 하나의 프로세스만 메모리에서 동작이 가능하다. 만약 메모리의 용량을 초과한 프로그램을 실행시키려면?✅ 스왑 과정이 존재하기 때문에 실제 메모리에 전체 프로세스가 올라가있는 것에 비하면 속도가 느리다.메모리 오버레이: 실행시킬 프로세스를 분할시켜, 사용되는 부분을 메모리에 올리고 나머지는 하드디스크 스왑 영역에 저장 ❓ 멀티 프로그래밍 환경에서는 어떻게 동작?1. 가변 분할 방식(세그멘테이션): 프로세스가 크기에 따라 메모리를 분리- 연속된 메모리 공간에 할당되기에 내부 단편화 현상 없음- 외부 단편화 발생: 연속된 공간에 할당을 해야하니 5MB 프로그램을 3MB와 2MB 공간에 할당이 불가하다.2. 고정 분할 방식(페이징): 프로세스 크기 상관없이 메모리 분리 (만약 5MB 프로그램을 2MB 고정 분할 메모리에 저장시키려면 2 / 2 / 1로 분리) -> 비연속 메모리 할당- 구현이 간단하고 오버헤드가 적음- 내부 단편화 발생: 위 예시에서 2MB 분할 공간에 1MB만 사용3. 버디 시스템(가변 + 고정): 2의 승수로 메모리를 분할하여 할당- 전체 메모리 영역을 하나의 프로세스가 올라갈 수 있을 정도로 2의 승수로 나눠서 분할- 최소한의 내부 단편화, 간단한 메모리 합치기 조각 모음 : 외부 단편화에서 분리된 메모리 공간을 합치는 작업 - 실행 중인 프로세스를 일시적으로 멈춰야 해서 오버헤드 발생 📌 더 찾아본 점IPC(Interprocess Communication)란?프로세스 간 협업을 위해 데이터를 공유하기 위해서는 IPC 기술이 필요하다 공유 메모리(Shared Memory): 공유 메모리 지역을 지정하고 프로세스들은 공유 메모리에 접근하여 데이터를 공유한다. (producer-consumer problem)메세지 패싱(Message Passing): 협력하는 프로세스 간 메세지 전달을 통해 데이터 동기화(communication link)를 통해서 각 프로세스가 소통한다. race condition이란?다수의 프로세스(혹은 쓰레드)가 같은 데이터를 동시에 접근하거나 처리하면, 실행되는 순서에 따라서 결과가 달라진다.이를 해결하려면 특정 시간에 하나의 프로세스만 공유 자원을 다뤄야 한다. 즉, 프로세스는 동기적으로 실행되어야 한다.1. 상호배제(Mutual Exclusion)을 보장해주어야 한다.- 한 프로세스가 "임계영역(citical section)"을 실행 중일 때, 다른 프로세스는 임계 영역을 실행할 수 없다.2. 데드락(deadlock)을 회피(진행)- 임계 영역에 들어갈 프로세스를 정하는 건, 임계 영역에 들어가야하는 프로세스들만 참여할 수 있다. - 영역에 들어가는 과정이 무한정 지연되는 것을 방지3. 유한 대기(Bounded Waiting) (starving 기아 상태 방지)- 임계 영역에 들어가기를 요청한 프로세스는 무한정 기다리면 안된다. 상호배제 메커니즘Mutex Locks: 임계 영역에 진입하면 lock을 acquire()와 release()를 통해 주고 받음Semaphore: 공유자원 수용가능 수에 따라 정수형 변수 s 변수를 초기화. wait(s)와 signal(s)를 통해 각 작업에서 s값을 중가, 차감 자원할당 그래프(Resource0Allocation Graph)운영체제에서 자원이 할당되어 있는 상태를 그래프로 그려, 시각적으로 데드락의 여부를 판단- T = {T1, T2, ..., Tn}: 실행 중인 쓰레드- R = {R1, R2, ..., Rm}: 할당될 자원 타입- T_i → R_j: i 쓰레드가 j 자원을 요청한다- R_j → T_i: j 자원을 i 쓰레드에 할당되어 있다.위 예제에서는 3개의 쓰레드와 4개의 자원이 있으며 2개의 순환 구조를 갖고 있다.T1 → R1 → T2 → R3 → T3 → R2 → T1T2 → R3 → T3 → R2 → T2모든 쓰레드는 하나의 자원을 점유 중인 상태에서 다음 자원을 점유하기 위해 대기 상태에 있고, 이러한 상태가 원형을 이루어지고 있기 때문에 데드락에 빠질 확률이 높다고 판단한다. 메모리 주소 바인딩1. symbolic address: 소스 프로그램에 변수와 같이 메모리 주소를 상징적으로 저장2. relocatable address: 실제 메모리 주소가 결정될 수 있는 재배치 가능 주소3. absolute address: 컴파일 타임에 결정되는 메모리 주소 1. compile time: 컴파일 시점에 물리 주소가 결정 (`absolute address`로 결정)2. load time: 컴파일 시점에 메모리 위치가 확정나지 않았다면, relocatable code로 변환. 프로세스가 로드되어 메모리에 올라갈 때 물리 주소가 결정3. execution time: 프로그램 실행 도중에 메모리 주소가 변경될 수 있다면 런타임까지 바인딩 지연. MMU에 의해서 논리 주소를 물리 주소 결정 📌 백엔드 면접 질문해보기데드락과 라이브락의 차이점?데드락은 두 개 이상의 쓰레드가 자원을 점유하고 있지만 다른 자원을 점유하기 위해 대기 중인 교착 상태를 의미합니다. 라이브락이란 두 개 이상의 쓰레드가 충돌을 회피하기 위해 실패 작업을 반복하지만 진전이 없는 상태를 의미합니다.데드락은 교착 상태에서 모든 프로세스가 멈춰버리지만 라이브락은 실패 작업을 계속 시도하기 때문에 멈추지는 않습니다. 락 기반 동기화와 락 프리 알고리즘이란?락 기반 동기화란 하나의 프로세스만 공유자원을 사용하도록 강제하여 동기화시키는 방법입니다. 뮤텍스나 세마포어가 락 기반 동기화에 해당됩니다. 락 기반 기법은 두 개 이상의 스레드가 서로 상대방의 락의 해제를 기다린다면 데드락이 발생할 수 있으며, 낮은 우선순위의 스레드가 자원을 먼저 점유하면 높은 우선순위의 스레드가 대기를 해야하는 우선순위 역전 등의 문제가 있습니다.락 프리 알고리즘이란 락을 사용하지 않고도 동기화를 유지시킬 수 있는 알고리즘입니다. 해당 알고리즘은 CAS(Compare And Swap) 같은 원자적 연산을 통해 동기화를 보장하는데, 원자적 연산이란 연산이 중간에 중단없이 시작되지 않거나 완전히 수행되는 것(all or nothing)을 의미하며 CAS는 해당 원자적 연산을 기반으로 현재값을 읽고 예상했던 값과 일치할 경우에만 값을 변경하며 일치하지 않는다면(다른 쓰레드의 개입) 업데이트하지 않는 방식입니다. Node.js에서 데드락이 발생할 수 있을까? 공식 문서에 따르면 Node.js에는 락이 존재하지 않아서 데드락이 발생될 확률이 매우 적다고 적혀있습니다. 다만 libuv에서 제공하는 동기적인 메서드를 실행할 때, js파일에 대해 대기 상태가 발생할 수 있는데 이는 데드락이라기 보다는 Blocking에 해당합니다. 또한 이러한 동기 I/O 메서드에 대해서도 콜백을 포함한 비동기 처리 함수가 함께 제공됩니다. (readFileSync / readFile) V8 엔진 메모리 관리 방식은? V8 엔진은 node.js의 실행 엔진이며 컴파일과 GC를 포함하고 있다.V8 메모리 구조(Resident set)은 크게 Heap과 Stack 메모리로 구분된다.Heap에는 크게 New Space와 Old Space가 존재하며 New Space는 짧은 생명주기를 갖는 새로 생성된 객체들이 저장되며 2번의 minor GC에도 제거되지 않은 객체는 Old Space로 이동되어 저장된다. New Space는 minor GC, Old Space는 Major GC로 참조되지 않는 변수들을 가비지 컬렉팅 한다. Buffer와 Stream의 메모리 사용 방식 차이? Buffer: 데이터를 조각(chunk)내어 buffer에 다 차우면(buffering) 일괄로 데이터를 전송. - 메모리의 사용 비율이 크지만 데이터 처리 속도가 크게 향상Stream: 데이터 청크와 버퍼의 크기를 작게하여 지속적으로 데이터를 전달하는 방식 - 메모리 사용이 적지만 데이터 실시간 효율성이 높아짐 📔 회고🚀 최종 목표 : 더 효율적인 백엔드 개발을 위해 기본적인 운영체제 지식들을 확실히 잡아가기🚀 매주 규칙:각 섹션마다 하나의 .md 파일을 생성하고, 섹션 내 각 유닛은 헤더로 구분강의를 듣고 최대한 이쁘게 (?) 정리해놓기 매 강의를 듣고 궁금하거나 이해가 안가는 부분은 추가적으로 더 찾아서 정리해두기매 강의 내용을 백엔드 관점에서 고민해보고 GPT와 대화를 통해 정리첫 주차 발자국과 미션을 수행한 후에 이번 주에는 더 구체적이고 집중적으로 강의를 듣고 복습할 수 있게 방식을 수정하였다.규칙 3번에서 매 강의를 듣고 궁금하거나 이해가 안가는 부분에 대해서는 보통 검색을 통해 블로그를 찾아보고 정리하였었다. 하지만 블로그에 적혀있는 글마다 내용이 다르기도 하고 궁금증이 해소되지 않는 부분들이 있어, 더 찾아보던 도중 CS로 유명한 공룡책(Operating Systerm Concepts) 무료 영문 pdf파일을 발견하였고 그 날 강의에 해당하는 주제에 맞춰 공룡책을 공부하고 개념을 탄탄히 하는 것에 집중하였다. 추가적으로 인프런 강의 중에 무료로 공룡책에 대한 내용을 설명해주는 강의도 있어 함께 공부를 하니 더 깊이 있고 쉽게 개념을 잡아갈 수 있었던 것 같다.규칙 4번에서 처음에는 백엔드 관점으로 강의 내용을 살펴보려 하였는데, 생각보다 모호하고 막연하다고 생각하여 규칙을 조금 변경하였다. GPT에는 Node.js 전문가 프롬프트가 제공되어 있어서, 해당 프롬프트를 이용하여 오늘 배운 내용에 대해 이야기하고 백엔드 면접 질문을 리스트로 달라고 요청하면 기초부터 고급 그리고 node.js를 활용한 심화 질문까지 리스트업해준다. 해당 부분에 대해서 스스로 답변해보고 답변하지 못한 부분이나 설명이 부족한 부분은 다시 AI와 대화하면서 지식을 채울 수 있었던 것 같다.결과적으로 3. 매 강의 듣고 해당 내용 공룡책 & 강의 1회 정독 및 수강 / 4. 매 강의에 대한 내용 백엔드 면접 질문을 추려서 답변하고 개념 구체화하기 이렇게 수정하여 2주차를 진행하였다. 기존 독학으로 CS를 공부하였을 때는, 그 범위가 너무 넓어 어떤 부분을 공부하고 있는지도, 어디까지 공부해야할 지도 전혀 감을 잡을 수 없었는데 이렇게 강의를 수강하고 그 강의의 깊이있는 개념까지 찾아가며 공부하다보니 확실히 체계가 잡히고 개념도 탄탄히 공부해 볼 수 있는 것 같다. 해당 방식으로 공부하면 딱 몰입할 수 있을만큼 적당히 공부하는 것 같아서 다음 주에는 추가적인 규칙을 넣기보다 기존 규칙을 유지해보려고 한다. 

시스템 · 운영체제인프런워밍업운영체제발자국

강동훈

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

💻 알고리즘📌 재귀✅ 재귀 : 어떠한 것을 정의할 때 자기 자신을 참조하는 것✅ 콜스택 : 함수가 호출되면서 올라가는 메모리 영역 (메모리 스택 영역) - FILO(First In Last Out)함수를 호출하면 콜스택에 올라가고 종료되면 콜스택에서 종료된다. 1. 다른 함수 2개를 호출하는 상황1. A가 실행되고 콜스택에 올라간다2. A가 종료되고 콜스택에서 제거된다3. B가 실행되고 콜스택에 올라간다4. B가 종료되고 콜스택에서 제거된다2. 하나의 함수에서 다른 함수를 호출하는 상황1. A가 실행되고 콜스택에 올라간다.2. A 실행 중, B를 실행하고 콜스택에 올라간다.3. B가 종료되고 콜스택에서 제거된다.4. A가 종료되고 콜스택에서 제거된다.3. 재귀함수1. A-1이 실행되고 콜스택에 올라간다.2. 내부에서 기저 조건을 확인하고 거짓이라면 다음 문장을 실행시킨다.3. 재귀에 의해 A-2가 실행하고 콜스택에 올라간다.4. ...5. 기저 조건에 의해 함수가 종료된다.6. 가장 상위 함수부터 콜스택에서 제거된다.7. 만약 기저조건이 없었다면 무한으로 콜스택에 올라가 메모리 부족 현상 발생 📌 재귀적으로 생각하기1. 반복문을 재귀함수로 대체하는 것은 더 나쁜 효율성을 갖는다. (콜스택 메모리 차지)2. 하위 문제를 기반으로 현재 문제를 계산한다.3. 하향식 계산은 재귀함수만 할 수 있다. 📌 버블 정렬- 앞에 있는 데이터와 옆 데이터를 비교하여 자리를 바꾸고 정렬하는 방식- 성능이 아쉬움(O(n^2)) 📌 선택 정렬- 정렬되지 않은 영역의 첫 원소를 마지막 원소까지 비교하여 정렬하는 방식- 성능이 아쉬움(O(n^2)) 📌 더 찾아본 점❓ 하노이 탑✅ 재귀적으로 생각해봐야할 부분1. 마지막 원판(count) 위의 모든 원판은 temp로 가야한다. (to -> temp)2. 마지막 원판은 to 로 간다3. temp에 있는 모든 원판은 to로 가야한다.(from -> temp, temp -> from) ❓ 하노이 탑 (count: 4) 진행과정{ 4 A C B { 3 A B C { 2 A C B { 1 A B C console.log(1 block from A to B) } console.log(2 block from A to C) { 1 B C A console.log(1 block from B to C) } } console.log(3 block from A to B) { 2 C B A { 1 C A B console.log(1 block from C to A) } console.log(2 block from C to B) { 1 A B C console.log(1 block from A to B) } } } console.log(4 block from A to C) { 3 B C A { 2 B A C { 1 B C A console.log(1 block from B to C) } console.log(2 block from B to A) { 1 C A B console.log(1 block from C to A) } } console.log(3 block from B to C) { 2 A C B { 1 A B C console.log(1 block from A to B) } console.log(2 block from A to C) { 1 B C A console.log(1 block from B to C) } } } } 📔 회고🚀 최종 목표 : 로드맵을 마무리하고 알고리즘 문제들을 제대로 이해할 수 있을 정도의 수준이 되기.🚀 매주 규칙:각 섹션마다 하나의 .md 파일을 생성하고, 섹션 내 각 유닛은 헤더로 구분강의를 듣고 최대한 이쁘게 (?) 정리해놓기매 강의를 듣고 궁금하거나 이해가 안가는 부분은 추가적으로 더 찾아서 정리해두기강의에 관련된 알고리즘 최소 1문제씩 풀어 습관화 이번 주차에는 알고리즘에 대한 이론과 정의보다는 실제 코드를 작성해보면서 동작 원리를 익히는 게 주된 주차였다. 그에 맞춰서 알고리즘에 대한 이론보다 동작되는 과정을 익히고 코드로 어떻게 풀어나가는 지 이해하기 위해 직접 코드를 작성해보고 어떻게 진행되는 지 생각해보고 직접 작성해보았다.재귀함수의 대표적인 문제인 하노이 탑의 경우, 강의를 직접 듣고 원리와 코드를 한 번에 이해하기가 힘들어서 검색해보고 애니메이션도 확인하면서 이해하려 노력하였다. 여러 방식을 익혀봤지만 역시 동작되는 과정을 직접 타이핑해보는 것이 가장 이해가 빨랐던 것 같다.(하노이 탑 진행과정 코드) 이 뿐만 아니라 하노이 탑 시뮬레이터(라고 말하고 게임)도 있길래, 미리 동작 과정을 예상해서 코드로 타이핑을 하고 시뮬레이터에 그 코드 과정을 그대로 따라가면서 학습했던 것 같다.이번 주 목표 중 하나였던 알고리즘 최소 1문제 습관화 또한 달성하였다. 기존에는 백준이나 프로그래머스를 통해 문제를 풀어봤었는데 이번에는 영어 공부도 할 겸 leet 코드를 통해서 매일 알고리즘 문제를 풀어보았다. 매일 강의를 듣고 그 강의에서 배웠던 알고리즘 방식과 관련된 문제들을 찾아보고 최소 1문제씩 풀었다. 문제를 풀면서 AI 혹은 discussions에 나와있는 해설들을 절대 보지 않으려고 노력하였고 뭐가되든 첫 문제 풀이는 내 스스로 하는 것에 최대한 집중하였다. 그렇게 문제를 풀고 틀리게 되면 한 번 더 고민해보고 주변 도움을 구하였고, 만약 맞췄다면 그 이후 성능을 더 빠르게 문제를 풀 수 있는 방법이 있는지 찾아보기도 하였다.알고리즘 문제를 풀면서 다음 주에 해보고 싶은 규칙이 하나 생겼다. 현재는 내가 알고 있는 방식으로만 문제를 해결하고 있지만 직접 문제를 해결하지 않더라도 문제에 대해 알맞는 자료구조를 선택할 수 있는 능력을 기르는 것은 어떨까? 그래서 이 또한 GPT를 이용해서 여러 문제들에 대해 나열을 시키고, 그 문제를 해결할 수 있는 최적의 자료구조와 알고리즘을 답변하여 점점 더 개념을 탄탄히 하고 최적의 문제해결능력을 사고하는 능력을 길러보고 싶다. GPT를 통해 여러 문제를 리스트업하고 최적의 자료구조나 알고리즘을 맞춰보기   

알고리즘 · 자료구조자료구조알고리즘인프런워밍업

수뼈

인프런 워밍업 클럽 스터디 3기 - CS 전공지식(자료구조 & 알고리즘) <둘째 주 미션>

1. 재귀함수에서 기저조건을 만들지 않거나 잘못 설정했을 때 어떤 문제가 발생할 수 있나요?스택 오버플로우(Stack Overflow)가 발생합니다. 일반적으로 코드가 실행되면 Call Stack이 생성되어 함수가 호출될 때마다 그와 관련된 정보가 Stack Frame 형태로 메모리에 저장됩니다. 재귀함수에서 기저조건을 만들지 않거나 잘못 설정했다면 함수가 무제한으로 호출되며, 그에 따라 Stack Frame도 무제한으로 쌓이게 됩니다. 이렇게 늘어난 용량이 프로세스에 할당된 Call Stack 메모리의 잉여 용량을 초과하면 프로세스가 OS에 의해 강제 종료되는데, 이것이 스택 오버플로우(Stack Overflow)입니다.2. 0부터 입력 n까지 홀수의 합을 더하는 재귀 함수를 만들어보세요.function sumOdd(n) { if (n <= 0) return 0; if (n % 2 === 1) { 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 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); traverseDirectory2(filePath); // 재귀 호출 } else { console.log('파일:', filePath); } } } traverseDirectory("."); // 현재 경로의 모든 하위 경로의 파일, 디렉토리 출력

알고리즘 · 자료구조자료구조알고리즘

채널톡 아이콘