블로그

10

우빈님의 세심한 코드 리뷰 - 인프런 워밍업 클럽 3기 백엔드 코드 ✨

👣 발자국 책갈피인프런 워밍업 클럽 3기 백엔드 코드 발자국 1주차인프런 워밍업 클럽 3기 백엔드 코드 발자국 2주차인프런 워밍업 클럽 3기 백엔드 코드 발자국 3주차인프런 워밍업 클럽 3기 백엔드 코드 발자국 4주차✅미션PR 책갈피[미션 Day 2] 추상과 구체 예시 작성[미션 Day 4] 리팩토링 & SOLID 원칙[미션 Day 7] 리팩토링 연습[미션 Day 11] 단위테스트 작성[미션 Day 16] 레이어드 아키텍처 특징 및 테스트 작성법[미션 Day 18] Mock 어노테이션 종류 및 차이점 & BDD 패턴 매치🙊 시작이 반이다... 벌써 중간점검 ?인프런 워밍업 클럽이 시작한지 2주가 지났다.. 앞으로 남은 발자국이 2개 뿐이다.. 👣이번주에는 중간점검으로 온라인 라이브가 진행 되었다. 온라인 라이브에 대한 내용은 다음과 같다.미션 Day 4 에 대한 공통 피드백Q&A에 대한 답변미션 Day 7 코드리뷰 진행각 세션은 놀라울 정도의 세심한 우빈님의 피드백 덕분에 많은 인사이트를 얻게 되어 좋은 시간이었다. 나도 첫번째로 코드리뷰를 신청하고 라이브 마지막에 코드리뷰를 받았는데 너무 좋은 경험이었다. 💪(커피를 좋아하시기로 유명한 우빈님께 Q&A 세션 도중 저가 커피 브랜드 중 어디 브랜드가 제일 맛있냐는 질문이 나왔는데.. 드셔 보신 적이 없다고 답변해 주신 부분이 인상적이었다.. ㅋㅋ 😁)✨ 우빈님의 세심한 코드 리뷰위에서 이야기했듯이 코드리뷰를 첫번째로 신청해서 우빈님께 온라인 라이브 시간에 코드리뷰를 받았다.해당 미션은 "스터디 카페" 프로그램을 리팩토링하는 미션이였는데.. 작년 4분기에 강의를 수강했을 당시에 3번이나 진행하였다.첫 번째 리팩토링, 강의를 듣기 전에 리팩토링두 번째 리팩토링, 강의를 수강하며 리팩토링세 번째 리팩토링, 강의를 수강 후 정리하며 다시 리팩토링이번이 네 번째 리팩토링이였는데 할 때마다 왜 새로운 것인지.. 🥲그래도 손이 기억이라도 한 듯 나름 순조롭게(?) 미션을 진행하게 되었고, 추가적으로 리팩토링을 진행하였고 해당 부분을 리뷰를 받고 싶어 신청하게 되었다.(우빈님께서 4번이라는 부분에 놀라셨는지(?).. 디스코드 스레드에 댓글을 남겨주셨다! 🤣)다시 돌아와서.. 코드리뷰 받은 내용은 아래와 같다.1⃣ 중요 도메인 StudyCafePassType의 구조화 🔗 Github PR 링크StudyCafePassType 구조화 리팩토링 ♻ 리팩토링 코드public enum StudyCafePassType implements PassTypeSelectable, PassTypeFormatter { // 📝 인터페이스 구조화 HOURLY("시간 단위 이용권") { // 📝 사용자 입력에 대한 구조화 @Override public boolean selected(String userInput) { return "1".equals(userInput); } // 📝 사용자 출력 포맷에 대한 구조화 @Override public String format(StudyCafePass pass) { return String.format("%s시간권 - %d원", pass.getDuration(), pass.getPrice()); } } } ✏ 우빈님 리뷰Q. 클래스 내부에서 사용자 입력값 및 출력값에 사용하는 인터페이스를 구현함으로써 오버 엔지니어링이 된 것 같은 느낌이 드네요.. 🤦‍♂️ A. 저도 그렇게 생각해요.. ㅋㅋㅋㅋ 오버 엔지니어링이기보다 PassType은 중요한 도메인 모델인데, Input에서만 의미를 가지는 사용자 선택지가 침투하고 있다. 사용자 선택 방법이 "a", "b", "c"로 바뀐다면? 단순히 입력 방식을 바꿨을 뿐인데 무료 도메인 모델이 수정되어야 하는 엄청난 사태가 발생한다. 항상 구조화를 하는 것이 정답은 아니다. Output format도 마찬가지이다. 책임이 우선이다. 적절한 책임의 분배가 객체의 결합도를 낮추고 응집도를 높이는 것이다.  🤔 돌아보기단순히, OCP를 적용하기 위해 접근해서 리팩토링 했었는데..적절한 객체 책임 분리를 하지 못했으며, 중요한 도메인 모델을 수정하는 엄청 큰 사이드 이펙트가 일어날 수 있다는 점을 간과 했다는 것이다.다음부터는 구조화를 남발하지 않고 책임에 집중해서 리팩토링 해야겠다..2⃣ 이용권을 읽는 부분과 읽은 부분의 개념을 추출하여 객체 분리 🔗 Github PR 링크ReadLockerPasses 객체 분리 ♻ 리팩토링 코드public class ReadLockerPasses { // 📝 LockerPasses를 해석하는 객체 분리 private final List<StudyCafeLockerPass> passes; private ReadLockerPasses(List<StudyCafeLockerPass> passes) { this.passes = passes; } // 📝 lines을 해석하여 List<StudyCafeLockerPass> 객체를 만들어준다. public static ReadLockerPasses ofLines(List<String> lines) { List<StudyCafeLockerPass> passes = lines.stream() .map(ReadLockerPasses::ofLine) .toList(); return new ReadLockerPasses(passes); } private static StudyCafeLockerPass ofLine(String line) { String[] values = line.split(CSV_SPLITTER); // ⭐️ CSV라는 방식에 종속적 StudyCafePassType studyCafePassType = StudyCafePassType.valueOf(values[0]); int duration = Integer.parseInt(values[1]); int price = Integer.parseInt(values[2]); return StudyCafeLockerPass.of(studyCafePassType, duration, price); } // 📝 StudyCafeLockerPasses를 생성해준다. public StudyCafeLockerPasses toPasses() { return StudyCafeLockerPasses.of(passes); } } ✏ 우빈님 리뷰Q. 일급 컬렉션을 적용하기 위해 toPasses() 메서드를 생성했는데 Read~라는 네이밍을 가진 클래스에 많은 책임이 부여된 것 같아 네이밍이 모호한 것 같습니다. 좋은 방법이 있을까요..? 🧐 A. 많은 책임이라고 생각하신 이유가 있을까요? "ReadLockerPasses는 어디선가 읽은 lines를 가지고 StudyCafeLockerPasses를 만들어준다"의 책임으로 보여서, 어색하지 않으며 테스트 코드 작성도 가능하다. 그와 별개로 CSV라는 방식에 종속되어있다. CSV형식이 다른 방식으로 바뀌었을 때 같이 바뀌어야 하는 부분이 CSV_SPLITTER 부분이다. 의도한 것 이라면 상관없다.  🤔 돌아보기Read라는 클래스명을 가지고 있어 StudyCafeLockerPasses를 생성해주는 메서드가 존재해 많은 책임이 있다고 생각했는데..우빈님 리뷰 이후에 다시 보니.. 그렇게 어색한가 싶기도 하다.. ㅎㅎ해당 클래스의 작성 당시 CSV 방식을 의존하려는 의도는 없었다. 단순히 읽은 부분의 개념을 추출한 것인데.. 위의 클래스는 CSV_SPLITTER상수가 사용되어 의도하지 않게 CSV라는 방식에 종속적이게 된 것이다.CSV라는 방식이 변경되면 객체 로직이 바뀌어야 한다.객체 구현 시, 종속성에 대해서 방어적으로 접근할 필요가 있어보인다.3⃣ ProvideException 커스텀 예외🔗 Github PR 링크ProvideException 커스텀 예외 ♻ 리팩토링 코드// 📝 이용권을 가져오는 과정에서 생긴 에러의 커스텀 예외 클래스 생성 public class ProvideException extends RuntimeException { public ProvideException(String message) { super(message); } } public class StudyCafePassMachine { public void run() { try { outputHandler.showPassOrderSummary(order); } catch (AppException e) { outputHandler.showSimpleMessage(e.getMessage()); } catch (ProvideException e) { // 📝 커스텀 예외 catch outputHandler.showSimpleMessage("이용권을 제공받을 수 없습니다."); } catch (Exception e) { outputHandler.showSimpleMessage("알 수 없는 오류가 발생했습니다."); } } } ✏ 우빈님 리뷰Q. AppException 성격이랑 다른 것 같다고 생각되어 Provider 인터페이스에서 발생하는 예외 클래스 ProvideException를 별도로 생성하였습니다. A. 혹시 어떻게 다르다고 생각셨나요?? AppException의 의도는, 프로그램에서 발생할 수 있는 대부분의 애플리케이션 상황을 정의하는 최상위 예외 클래스이다. 만약 ProvideException을 별도로 표기하여 더 구체적인 상황을 나타내고 싶으면, AppException을 상속받아서 구성해야 한다. 그렇지 않으면 커스텀 예외 클래스가 늘어남에 따라 catch절도 같이 늘어날 것이다. 추가적으로, "이용권을 제공받을 수 없습니다."라는 메시지가 사용자 친화적이지 않다.  🤔 돌아보기리팩토링 당시, 초기 이용권을 가져와야만 프로그램이 실행된다는 관점에서 ProvideException의 커스텀 예외 클래스를 작성하였다.하지만 이용권을 가져오는 부분은 프로그램 내부에서 필요한 시점마다 호출하고 있어우빈님 리뷰대로 AppException 클래스를 상속받아서 작성하는 것이 더 나은 설계 같다.예외 메세지도 사용자 관점에서는 친화적이지 않은 것이 분명하다.내가 키오스크 시스템을 사용하다가 저런 메세지를 마주한다면... 화가 날 것 이다... 😡프로그램의 의도를 정확히 파악할 필요가 있어보인다. 또한 예외 메세지도 누가 보는지에 따라 고민해보는 습관을 길러야겠다.이렇게, 요청한 3개의 리뷰와 2개의 추가 리뷰를 받아 보았다..고작 3일 만에 7명이나 리뷰를 해주셨는데 세심하고 또 세심했다... 퀄리티가 상당했다.. ✨이번 온라인 라이브를 통해 우빈님에 대한 팬심과 존경심이 더욱 커졌다....! 📈리뷰해주신 내용으로 다시 리팩토링을 함으로써 한층 더 Readable Code에 대한 성장을 경험할 수 있었다. 🚀💡 자기만의 언어로 강의 키워드 정리하기 섹션 6. 코드 다듬기좋은 주석 - 주석의 양면성주석이 많다는 것 : 추상화가 덜 되고 가독성이 좋지 않은 코드 (코드 품질 저하 📉)주석이 필요한 경우 : 히스토리를 알 수 없을 경우, 주석으로 상세히 설명변수와 메서드 나열 순서변수 : 사용하는 순서대로 위치한다. (인지적 경제성 / 뇌 메모리 줄이기)객체의 공개/비공개 메서드 : 공개 메서드를 상단에 위치하고, 비공개 메서드 하단에 위치한다. 공개 메서드 중에서도 중요도의 순서에 따라 배치한다.공개 메서드 : 객체의 상태를 변경 하는 부분이 가장 상단에 위치하도록 - 상태 변경 >>> 판별 >= 조회비공개 메서드 : 출현한 순서대로패키지 나누기여러 파일들의 네임 스페이스를 관리하기 때문에 적당한 수준으로 잘 나누어야 한다.대규모 패키지 변경은 팀원과의 합의 필요 -> 추후 conflict가 생길 수 있다.기능 유지보수하기정렬 단축키, linting, style - sonarlint, editorconfig섹션 7. 리팩토링 연습메서드 추출로 추상화 레벨 맞추기Optionalreturn null / Optional 파라미터 사용은 안티패턴이다.객체에 메시지 보내기객체를 존중하고 메시지를 보내자.객체의 책임과 응집도⭐️ 추상화 관점의 차이 - FileHandler구현에 초점을 맞춘 추상화 VS 도메인 개념에 초점을 맞춘 추상화File을 read하는 부분의 로직들은 전부 FileHandler에 들어갈 것이다. 잘못된 객체 응집일 수도 있다..방법에 초점을 맞춘 설계 방식이 아닌 어떤 데이터를 가져오는 가에 대한 초점을 맞추는 것이 좋다.섹션 8. 기억하면 좋은 조언들능동적 읽기가지고 있는 리팩토링 기법들을 총동원해서 읽자. -> 리팩토링하면서 읽기눈으로만 보는 수동적 읽기는 권장하지 않는다.도메인 지식을 늘리기 위해서 능동적 읽기가 필요하다. (작성자의 의도 파악)오버 엔지니어링필요한 적정 수준보다 더 높은 수준의 엔지니어링예시 1. 구현체가 하나인 인터페이스구현체가 수정할 때마다 인터페이스도 수정해야 함코드 탐색의 어려움예시 2. 너무 이른 추상화정보가 숨겨지기 때문에 복잡도가 높아진다.후대 개발자들이 선대의 의도를 파악하기가 어렵다.은탄환은 없다클린 코드도 은탄환이 아니다.실무에서의 줄다리기지속 가능한 소프트웨어 품질 VS 기술 부채를 안고 가는 빠른 결과물 -> 클린 코드를 대비한 코드 센스가 필요하다.모든 기술과 방법론은 적정 기술의 범위 내에서 사용되어야 한다.항상 정답인 기술은 없다.한계까지 연습해보고, 적정 수준, 적정 시점을 깨닫는 것이 필요하다.섹션 3. 단위 테스트단위 테스트작은 코드(클래스 또는 메서드) 단위를 독립적으로 검증하는 테스트 -> 가장 기본이 되는 테스트검증 속도가 빠르고, 안정적수동 테스트, 자동화 테스트 -> 인지 필요사람이 검증하는 수동 테스트 -> sout으로 출력하고 눈으로 직접 확인기계가 검증하는 자동화 테스트Junit5, AssertJJunit5 : 단위 테스트를 위한 테스트 프레임워크AssertJ : 테스트 코드 작성을 원할하게 돕는 테스트 라이브러리 - 풍부한 API 메서드 체이닝 지원해피 케이스, 예외 케이스 -> 테스트 케이스 세분화예외 케이스 : 암묵적 혹은 드러나지 않은 요구사항에서 발견경계값 테스트범위, 구간, 날짜 경계값들로 테스트를 해야한다.테스트하기 쉬운/어려운 영역 (순수함수)테스트 하기 어려운 영역을 구분하고 분리하기외부로 분리할수록 테스트 가능한 코드는 많아진다.테스트하기 어려운 영역관측할 때마다 다른 값에 의존하는 코드 : 현재 날짜/시간, 랜덤 값, 전역 변수/함수, 사용자 입력외부 셰계에 영향을 주는 코드 : 표준 출력, 메시지 발송, 데이터베이스에 기록하기순수 함수 - 테스트하기 쉬운 영역같은 입력에는 항상 같은 결과외부 세상과 단절된 형태lombok@Data, @Setter, @AllArgsConstructor 지양양방향 연관관계 시 @ToString 순환 참조 문제섹션 4. TDD: Test Driven DevelopmentTDD 테스트 주도 개발 (Test Driven Development)로, 프로덕션 코드보다 테스트 코드를 먼저 작성하여 테스트가 구현 과정을 주도하도록 하는 방법론선 기능 구현, 테스트 작성의 문제점 (일반적인 개발) - 구현순서 : 기능 -> 테스트테스트 자체의 누락 가능성해피 케이스만 검증할 가능성잘못된 구현을 다소 늦게 발견할 가능성선 테스트 작성, 기능 구현 (TDD) - 구현순서 : 테스트 -> 기능복잡도(유연하며 유지보수가 쉬운)가 낮은 테스트 가능한 코드로 구현할 수 있게 한다.테스트가 힘든 코드를 위한 코드 작성이 가능예를 들면 LocalDateTime.now()의 경우 외부세계로 분리해서 테스트를 하기 편한 코드를 작성할 수 있다.프로덕션 코드를 작성한 후 테스트 코드를 작성하기 귀찮을수도..쉽게 발견하기 어려운 엣지 케이스를 놓치지 않게 해준다.구현에 대한 빠른 피드백을 받을 수 있다.과감한 리팩토링이 가능해진다.테스트 코드가 보장TDD의 관점이전의 관점 : 테스트는 구현부 검증을 위한 보조 수단변화된 관점 : 테스트와 상호 작용하며 발전하는 구현부레드 - 그린 - 리팩토링Red : 실패하는 테스트 작성Green : 테스트 통과 하는 최소한의 코딩Refactor : 구현 코드 개선 테스트 통과 유지애자일 방법론 vs 폭포수 방법론애자일 방법론 https://agilemanifesto.org/iso/ko/manifesto.html반복적 개발(Iterative Development): 짧은 개발 주기(스프린트)를 반복하며 지속적으로 개선.유연성: 요구사항 변경을 수용할 수 있도록 유동적으로 진행.고객 참여: 개발 과정에서 지속적인 피드백을 반영하여 사용자 중심 개발 가능.자율적인 팀 구성: 개발팀이 자체적으로 의사 결정을 하며, 빠르게 문제를 해결함.폭포수 방법론단계적 개발: 요구 분석 → 설계 → 구현 → 테스트 → 배포 → 유지보수 순서로 진행됨.문서 중심: 각 단계마다 문서화가 철저하게 이루어짐.선형 구조: 이전 단계가 완료되어야 다음 단계로 넘어갈 수 있음.변경이 어려움: 초기에 요구사항을 확정하면 이후 변경이 어렵고 비용이 많이 듦.익스트림 프로그래밍XP(Extreme Programming, 익스트림 프로그래밍)는 애자일 방법론 중 하나로, 빠른 개발 주기와 지속적인 피드백을 중심으로 하는 소프트웨어 개발 방법론이다. 고객의 요구사항 변화에 빠르게 대응할 수 있도록 짧은 개발 반복 주기(Iteration)와 강한 협업 문화를 강조한다.스크럼, 칸반스크럼애자일 프레임워크로, 일정한 스프린트 동안 작업을 계획하고 진행하는 반복적이고 점진적인 개발 방식이다. 짧은 개발 스프린트를 통해 빠르게 결과물을 만들고 지속적으로 개선하는 것이 핵심이다.1⃣ 백로그 작성 – 제품 백로그에 모든 요구사항을 정리2⃣ 스프린트 계획 – 스프린트 기간 동안 수행할 작업 선정3⃣ 스프린트 진행 – 개발 진행 및 매일 스탠드업 미팅4⃣ 스프린트 리뷰 – 개발 완료된 기능을 검토5⃣ 회고(Retrospective) – 개선점을 찾고 다음 스프린트에 반영칸반Workflow와 가시성을 중심으로 한 애자일 프레임워크로, 지속적인 개선과 작업량 관리를 중점적으로 다룬다. 작업을 시각적으로 표현하여 현재 진행 상황을 쉽게 파악할 수 있도록 합니다.1⃣ Backlog: 해야 할 작업을 모아둠2⃣ To Do: 현재 진행할 작업3⃣ In Progress: 진행 중인 작업4⃣ Review/Test: 리뷰나 테스트가 필요한 작업5⃣ Done: 완료된 작업섹션 5. 테스트는 []다.테스트 코드는 문서다.프로덕션 기능을 설명해주는 것이 테스트 코드 문서다.다양한 테스트 케이스를 통해 프로덕션 코드를 이해하는 시각과 관점을 보완할 수 있다.고민했던 내용(테스트 코드)을 팀 자산(소스 코드)으로 공유할 수 있다.@DisplayName - 도메인 정책, 용어를 사용한 명확한 문장메서드명만으로 어떤 것을 검증하고자 하는 의도 파악이 어려움Junit5에 추가한 어노테이션이다.문장 형태로 섬세하게 테스트 검증에 대한 내용을 어노테이션안에 작성한다.섬세한 DisplayName특정 시간 이전에 주문을 생성하면 실패한다. ❌영업 시작 시간 이전에는 주문을 생성할 수 없다. ✅도메인 용어를 사용하여 추상화된 내용을 담기 -> 메서드 자체의 관점 보다 도메인 정책 관점 (특정 시간 -> 영업 시작 시간 ✅)테스트의 현상을 중점으로 기술하지 말 것 (~실패한다 ❌)Given / When / Then - 주어진 환경, 행동, 상태 변화Given : 시나리오 진행에 필요한 모든 준비 과정When : 시나리오 행동 진행Then : 시나리오 진행에 대한 결과 명시, 검증TDD vs BDDBDDTDD에서 파생된 개발 방법시나리오 기반한 테스트 케이스 자체에 집중하여 테스트한다.Junit vs SpockSpock은 Groovy언어로 BDD 패턴을 적용해서 테스트 코드를 작성할 수 있다.언어가 사고를 제한한다.명확하지 못한 테스트 코드는 사고를 제한할 수 있다.문서로서의 테스트를 신경 쓸 필요가 있다. 🏃 돌아보며..미션과 발자국을 정신없이 진행하다 보니 벌써 남은 인프런 워밍업 클럽도 2주밖에 남지 않았다. 처음에 OT 라이브 당시 러너가 120명 정도였는데, 이번 중간 점검 라이브 때는 60명 정도로 줄어들었다.강의 내용 자체는 어렵지 않지만.. 2개의 강의(14시간 + 12시간)를 한 달만에 들으면서 미션과 발자국을 진행하는 건 쉽지 않아 보인다.. 나는 미리 강의를 수강해서 다행이다라는 생각이 든다.. 😅하지만, 쉽지 않은 만큼 성실히 참여한다면 단기간 내 성장하는 데 큰 도움이 될 것이다.그리고 이번 주에 코드리뷰를 신청하기 잘했다는 생각이 들었다. 🍀 위에서도 여러 번 언급했지만 우빈님의 세심한 리뷰 탓(?)에내가 미션을 수행하는 데 있어 우빈님보다 세심하게 집착 했었나..? 반성하게 된다... 😭 마지막 주차 온라인 라이브에서도 테스트 코드에 대한 코드리뷰가 진행된다고 한다.기회가 된다면 또 한 번 코드리뷰를 받아 단골 손님이 되고 싶다. 😂2주를 걸쳐, 읽기 좋은 코드의 스터디 과정은 이번주로 막을 내렸다.다음 주차부터는 테스트 코드에 대해 본격적으로 스터디하는 과정이 진행된다.남은 2주도 화이팅하며 좋은 성장을 이루길 기대해 본다. 🔥 끝으로, 3월 중순이 되니 이제 슬슬 봄 내음이 나는 것 같다.. 🌸얼어붙은 개발 시장에도 봄이 찾아왔으면 좋겠다.. 🧊 발자국 2주차 끄읕 ! [출처]인프런 워밍업 클럽 : https://www.inflearn.com/course/offline/warmup-club-3-be-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기발자국박우빈클린코드읽기좋은코드

10 6개월 전
양성빈(Robert)

[인프런 워밍업 스터디 클럽] 0기 세번째 발자국 (feat. 마지막 발자국 ㅠㅠ)

발자국어느덧 인프런 워밍업 스터디 클럽 마지막 주차가 다가왔다. 매우 즐겁기도 했지만 한편으로는 매우 아쉬운 마음이 너무 걸렸다. 이런 기회가 자주 있었으면 하는 마음으로 마지막 발자국(회고)를 시작해보겠다.강의 요약Day10. 객체지향과 JPA 연관관계조금 더 객체지향적으로 개발할 수 없을까?우리는 지난 시간까지 책 생성 API를 개발하고 대출과 반납기능까지를 개발완료하였다. 하지만 여기서 이런 의문사항이 들 수 있다. SQL 대신에 ORM을 사용하게 된 계기는 "DB 테이블과 객체는 패러다임이 다름" 때문이다. 우리가 사용하는 Java는 객체지향 언어이고 요즘 서비스 진행중인 웹 어플리케이션도 절차지향적이기 보단 객체지향적으로 구성되어 있는 코드들이 많을 것이다. (개인적인 뇌피셜) 그래서 우리가 20강에서 배운 스프링 컨테이너도 객체지향 설계라는 지점에서 출발하게 되었다. 즉, User 객체와 UserLoanHistory를 협업시킬 수 없을까? 즉, 대출기능을 개발할때 BookService가 UserLoanHistory 객체를 만들어 저장하고, 그것을 User객체가 가져오는 방식이였다. 뭔가 BookService를 거쳐가야한다는게 걸린다. 즉, BookService로직은 User객체가 가져와 사용하고 User객체가 직접 UserLoanHistory와 상호작용을 하면 좋을 것 같다. 반납기능도 대출기능과 동일하게 바꾸면 좋을 것 같다. 이렇게 바꾸려면 조건이 존재한다. User객체와 UserLoanHistory가 서로 존재한다는 것을 인지해야 한다. 이것을 위해 연관관계 개념이 등장하였다. 대표적으로 N:1 관계가 존재한다.🙋🏻 N:1 관계란?예시로 들어보자. 어느 한 교실에 여러명의 학생이 존재할 수 있다. 이 때 학생은 N이고 교실은 1이다 이것을 N:1관계라고 부를 수 있다.그럼 관계를 설정하고 나서 다음으로 할 일은 연관관계 주인이 누구인지 알아야한다. 현재 우리의 실습 소스에서 user와 user_loan_history의 테이블을 보면 아래와 같다.create table user ( id bigint auto_increment, name varchar(25), age int, primary key (id) );create table user_loan_history ( id bigint auto_increment, user_id bigint, book_name varchar(255), is_return tinyint(1), primary key (id) );여기서 연관관계 주인을 누구로 할까? 쉽게 생각해서 N:1관계에서 N쪽이 보통은 연관관계 주인이라고 생각하면 쉽다.그리고 연관관계 주인이 아닌쪽에는 mappedBy 속성을 추가해줘야 한다. mappedBy의 속성의 값으로는 관계에 설정된 클래스에 선언된 자신의 객체의 변수명을 적어주면 된다. 실제 코드를 살펴보면 아래와 같이 변경이 가능하다.User.java@Entity public class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id = null; @Column(nullable = false, length = 20) private String name; private Integer age; @OneToMany(mappedBy = "user") private List<UserLoanHistory> userLoanHistories = new ArrayList<>(); protected User() { } public User(String name, Integer age) { if (name == null || name.isBlank()) { throw new IllegalArgumentException(String.format("잘못된 name(%s)이 들어왔습니다.", name)); } this.name = name; this.age = age; } public Long getId() { return id; } public String getName() { return name; } public Integer getAge() { return age; } public void updateName(String name) { this.name = name; } }UserLoanHistory.java@Entity public class UserLoanHistory { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id = null; @ManyToOne private User user; private String bookName; private boolean isReturn; protected UserLoanHistory() { } public UserLoanHistory(User user, String bookName) { this.user = user; this.bookName = bookName; this.isReturn = false; } public void doReturn() { this.isReturn = true; } }위의 코드처럼 N쪽에 @ManyToOne 어노테이션을 붙여주고 관계를 맺는 객체를 선언해준다. 그리거 1쪽도 마찬가지로 관계를 맺는 객체를 선언해주고 위에 @OneToMany 어노테이션을 선언해준다. 이런 방식을 양방향 연관관계라고 부르며, 한쪽만 연관관계를 맺을 시 단방향 연관관계라고 부른다. 이렇게 연관관계의 주인의 값이 설정되어야만 진정한 데이터가 저장된다.그럼 BookService는 어떻게 변경을 하는지 살펴보자.BookService.java// 5. 유저와 책 정보를 기반으로 UserLoanHistory를 저장. this.userLoanHistoryRepository.save(new UserLoanHistory(user, book.getName()));이제 위와 같이 user의 id값을 저장하는게 아닌 user 객체를 직접 저장할 수 있다.JPA 연관관계에 대한 추가적인 기능들1:1 관계예를 들어 한 사람과 실거주지의 관계가 딱 1:1 관계이다. 그러면 연관관계 주인은 어느 객체일까? 설정하기 나름이지만 주어진 상황은 사람이 연관관계 주인이라 생각하는게 좋을 것이다. 그러면 코드로 표현하면 아래와 같을 것이다.Person.java@Entity public class Person { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id = null; private String name; @OneToOne private Address address; public Long getId() { return id; } public String getName() { return name; } public Address getAddress() { return address; } public void setAddress(Address address) { this.address = address; this.address.setPerson(this); } }Address.java @Entity public class Address { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String city; private String street; @OneToOne(mappedBy = "address") private Person person; public Long getId() { return id; } public String getCity() { return city; } public String getStreet() { return street; } public Person getPerson() { return person; } public void setPerson(Person person) { this.person = person; } }🔥 연관관계 주인 효과연관관계 주인을 설정하는 것은 객체가 연결되는 기준이 된다.1. 상대 테이블을 참조하고 있으면 연관관계의 주인2. 연관관계의 주인이 아니면 mappedBy를 사용3. 연관관계의 주인의 setter가 사용되어야만 테이블 연결즉, 아래처럼 setter를 이용하여 연결이 가능하다.@Transactional public void savePerson() { Person person = this.personRepository.save(new Person()); Address address = this.addressRepository.save(new Address()); person.setAddress(address); }⚠ 주의만약 트랜잭션이 끝나지 않았을 때 한쪽만 연결해두면 반대쪽은 알 수 없다. 그래서 위의 코드에서 address.getPerson()을 출력을 하면 null이 뜰 것이다. 왜냐하면 지금은 현재 person만 address를 연결해줬기 때문이다. address는 person을 연결해주지 않았기 때문이다.그럼 해결책은 없을까? 객체안에 연관관계 편의 메서드를 만들어 두 객체의 setter를 호출하면 해결이 된다.N : 1 관계 - @ManyToOne과 @OneToMany위에서 언급을 했지만 @ManyToOne과 @OneToMany는 둘다 양방향으로 연결을 할 수 있지만 단방향 연결도 가능하다. 또한 이 어노테이션들을 이용하면서 새롭게 배우는 어노테이션이 있는데 바로 @JoinColumn이다.@JoinColumn- 연관관계의 주인이 활용할 수 있는 어노테이션.- 필드의 이름이나 null 여부, 유일성 여부, 업데이트 여부 등을 지정- 일종의 @Column 어노테이션과 유사하다고 생각하면 좋다.N : M 관계 - @ManyToMany구조가 복잡하고, 테이블이 직관적으로 매핑되지 않아 사용하지 않는 것을 추천한다고 하셨다. 실제로 실무에 근무하는 분들한테 이야기를 들으면 N:M은 많이 사용하지 않고 꼭 이런식으로 처리해야할 경우면 N:1과 1:N으로 풀어쓴다고 하셨다.cascade 옵션 & orphanRemoval 옵션한 객체가 저장되거나 삭제될 때, 그 변경이 폭포처럼 흘러 연결되어 있는 객체도 함께 저장되거나 삭제되는 기능.JPA에는 Entity들 사이의 연관관계를 정의할 때 사용할 수 있는 영속성 전이라고 하는 Cascade 옵션이 있다. 이 옵션을 이용해서 부모에 가해지는 변화를 자식에게 전파할지에 대해 설정할 수 있다.@OneToMany로 자식들을 갖고 있는 부모 객체만 저장/삭제 해도 자식 객체도 함께 저장/삭제 된다던지, 하는 효과를 누릴 수 있다.JPA에는 Entity들 사이의 연관관계를 정의할 때 사용할 수 있는 옵션 중에 orphanRemoval 라는 것이 있다. 이 옵션을 이용하면 부모가 자식에 대한 참조를 끊을 때, 참조가 끊어진 자식 Entity(고아 객체)를 DB에서 삭제하도록 설정할 수 있다.만약 어떤 회원이 책 2권을 대출했다고 하자. 그리고 그 회원이. 갑자기 회원탈퇴를 해서 DB에서 사라졌다. 그럴 경우 많이 이상하게 책 2권이 연결되어 있던게 끊어진 상태가 된다. 이상한 구조일 것이다. 즉, 회원이 삭제될 때 유저 대출기록도 같이 삭제해두는게 좋을 것이다. 그리고 이와 같이 쓰는 옵션이 바로 orphanRemoval 옵션이다.@OneToMany(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true) private List<UserLoanHistory> userLoanHistories = new ArrayList<>();위의 코드처럼 사용하면 부모객체의 저장/삭제해도 자식 객체도 함께 전파되면 삭제시, 자식 객체도 같이 삭제된다.책 대출/반납 기능 리팩토링과 지연로딩이제 우리가 만든 대출과 반납기능을 리팩토링 해보자. 리팩토링 할 부분은 무엇일까? 현재 코드를 보면 도메인 계층에 비즈니스 로직이 들어가져 있다. 또한 여기서 영속성 컨텍스트 4번째 옵션이 나오는데 바로 지연로딩이다.데이터를 처음에 한번에 로딩을 안하고 꼭 필요한 순간에 데이터를 로딩시킨다. 바로 @OneToMany의 fetch옵션의 default 값이다. 지연 로딩을 사용하게 되면, 연결되어 있는 객체를 꼭 필요한 순간에만 가져온다.그러면 우린 이제까지 연관관계를 맺고 주인을 정하고 지연로딩, cascade, orphanRemoval옵션을 이용해서 리팩토링과정을 거쳐보았다. 이렇게 연관관계를 이용하면 뭐가 좋을까?📖 연관관계 장점1. 각자의 역할에 집중할 수 있다. = 응집성2. 새로운 개발자가 코드를 봤을 때 이해가 쉬워진다.3. 테스트 코드 작성에 용이하다.그러면 무조건 연관관계 맺는것이 좋을까? 그렇지는 않다! 연관관계를 남발해서 사용하면 지나치게 사용하면, 성능상의 문제가 생길 수도 있고 도메인 간의 복잡한 연결로 인해 시스템을 파악하기 어려워질 수 있다. 또한 관계가 복잡하면 하나의 테이블 수정 시, 다른 테이블까지 영향을 끼칠 수 있다. 강의중에서도 코치님께서 아래와 같이 말씀주셨다.비즈니스 요구사항, 기술적인 요구사항, 도메인 아키텍처 등 여러 부분을 고민해서 연관관계 사용을 선택해야 한다.Day11. 기본적인 배포를 위한 준비배포란 무엇인가?배포란 무엇일까? 배포는 제 3자의 사용자가 우리가 만든 서비스를 전달하는 과정이라고 볼 수 있다. 우리는 지금 현재 우리의 개인 PC에다가 개발을 하고 웹을 띄워보며 테스트를 해보았다. 하지만 영희가 우리의 서비스를 이용할려하면 어떻게 할까? 현재 상황에서는 나한테 연락을 하고 우리집에 방문해서 사용하고 가야할 것이다. 물론 영희 1명이고 내가 집에 있다고 한다면 가능하다. 하지만 영희 혼자가 아니라 100만명이 우리의 서비스를 이용한다면 정말 고민이 많을 것이다. 또한, 내가 잘때 갑자기 철수가 오겠다고 하면 나는 잠을 자지도 못하고 철수가 우리집에 올 때까지 기다려야 할 것이다.그래서 나는 좋은 생각을 한다. 제3의 컴퓨터를 빌려서 우리의 웹 어플리케이션을 띄우는 것이다. 그리고 나의 친구들에게 그 컴퓨터 IP주소를 알려주면 된다. 이 과정을 배포과정이라고 한다. 그러면 이 컴퓨터는 누구한테 빌릴까? 네이버, 구글등 다양한 컴퓨팅 서비스를 해주는 곳은 많지만 대부분 아마존을 이용한다. 또한 배포를 위해 컴퓨터를 빌릴때 운영체제를 선택도 해야한다.profile과 H2 DB여기서 우리는 문제를 직면한다. 우리의 코드를 제3의 컴퓨터에서 실행시킬 때 DB같은 자원정보를 변경해줘야 한다. 이런 불편함에 이런 생각을 하게 된다. 코드변경 없이 우리의 컴퓨터에서 실행할때 우리의 DB가 연결이 되고 제3의 컴퓨터에서 실행할때는 제3의 컴퓨터에 설치된 DB가 연결되어야 한다. 즉, 똑같은 코드로 실행환경에 따라 설정을 다르게 하고 싶다. 이때 바로 profile을 이용하는 것이다. 현재 우리는 지금 profile이라는 것을 사용하고 있다. 바로 "default" profile을 사용한다. 아무것도 설정을 안하면 해당 프로필이 자동으로 올라온다. 그럼 실제 우리의 코드에 profile을 적용해보자. 똑같은 서버 코드를 실행시키지만, local 이라는 profile을 입력하면, H2 DB를 사용하고 dev 라는 profile을 입력하면 MySQL DB를 사용하게 바꾸자.🤔 H2 DB란?경량 Database로, 개발 단계에서 많이 사용하며 디스크가 아닌 메모리에 데이터를 저장할 수 있다. 또한, 개발 단계에서는 테이블이 계속 변경되는데 어차피 데이터가 휘발되기 때문에 ddl-auto 옵션을 create로 주면 테이블을 신경쓰지 않고 코드에만 집중할 수 있다! 그래서 개발단계나 테스트에서 H2 DB를 많이 사용한다.그러면 적용한 yml은 아래와 같이 될 수 있다.pring: config: activate: on-profile: local datasource: url: "jdbc:h2:mem:library;MODE=MYSQL;NON_KEYWORDS=USER" username: sa password: driver-class-name: org.h2.Driver jpa: hibernate: ddl-auto: create properties: hibernate: show_sql: true format_sql: true h2: console: enabled: true path: /h2-console --- spring: config: activate: on-profile: dev datasource: url: "jdbc:mysql://localhost/library" username: "root" password: "" driver-class-name: com.mysql.cj.jdbc.Driver jpa: hibernate: ddl-auto: none properties: hibernate: show_sql: true format_sql: true여기서 --- 은 프로필을 구분하는 표시선이라 생각하면 좋다. 그리고 DB 접속 url에 MODE=MYSQL;NON_KEYWORDS=USER 해당 옵션을 붙인 이유는 DB의 키워드중에 USER라는 것이 있기에 키워드로 설정 안하고 모드를 MySQL과 유사하게 만들기 위한 옵션이다. 또한 h2.console.enabled와 h2.console.path 옵션은 해당 경로로 접속했을 때 h2 console을 사용할 수 있기 위해서이다.git과 github이란 무엇인가?!개발 관련 서적이나 자료를 찾다보면 한번쯤 보이는 주소가 있다. 바로 git이다. git이란 코드를 쉽게 관리할 수 있도록 해주는 버전 관리 프로그램이다. 이런 상황이 있다하자. A개발자와 B개발자가 협업을 하고 있다 하자. 그리고 각자 개발 후 소스코드를 합칠때 문제가 생긴다. 다른 코드들은 상관없지만 같은 파일의 코드들을 다르게 수정할 우려가 있기 때문이다. 그래서 이것을 일일이 수작업으로 확인하기엔 너무 힘들다. 이래서 git이 등장한 것이다. 또한 버전을 관리하기에 아래와 같은 사태 또한 일어나지 않을 것이다.그러면 github는 무엇일까? git으로 관리되는 프로젝트들을 관리해주는 저장소이다. 우리는 git으로 관리하는 프로젝트를 github에 저장할 수 있다. 그럼 왜 github에 저장할까? 자랑용, 공유로 저장할 수 있지만 배포가 가장 큰 이유로 볼 수 있다. 제3자의 컴퓨터에 우리의 서비스를 배포해야하는데 우리의 소스코드를 usb나 외장하드에 담아 제3자의 컴퓨터까지 가서 복사해서 할 수는 없을 것이다. 만약 집 근처면 참고 갈테지만 만약 미국의 제3자의 컴퓨터가 있다면 비행기값이 더 나올 것이다. 깃 명령어그럼 간단하게 깃 명령어를 알아보자.📚 용어git init : git 프로젝트 시작하기git remote add origin [각자 저장소 주소]: git 프로젝트의 github 저장소 설정하기git add . : 코드들을 담는다. 일종의 택배상자에 담는다고 보면 된다.git status: 현재 택배상자에 코드들이 잘 담겨져 있는지 확인하는 명령어git commit -m "메세지" : 택배상자에 송장 붙이는 명령어git push : 택배상자를 github에 보내기 택배상자를 github에 보낼 때 git push –set-upstream origin master 명령어를 최초 1번 해줘야 한다. AWS의 EC2 사용하기AWS의 회원가입 로그인 과정을 거쳐서 제3자의 컴퓨터를 빌려보는 실습을 해보았다.Day12. AWS와 EC2 배포EC2에 접속을 하려면 아래와 같은 준비물이 필요하다.1) 우리가 접속하려는 EC2의 IP 주소2) 이전 시간에 다운로드 받았던 키 페어3) 접속하기 위한 프로그램 (git CLI 혹은 Mac terminal)다운로드 받은 키 페어를 이용하는 방법ssh –i 경로/키페어이름.pem ec2-user@IP다음으로 키페어 권한을 변경해주자.chmod 400 경로/키페어이름.pem아니면 위와같은 과정이 불편하다면 AWS의 콘솔을 이용하는 방법도 있다.리눅스 명령어mkdir : 폴더를 만드는 명령어ls : 현재 위치에서 폴더나 파일을 확인하는 명령어ls –l : 조금 더 자세한 정보를 확인할 수 있다!cd : 폴더 안으로 들어가는 명령어pwd : 현재 위치를 확인하는 명령어cd .. : 상위 폴더로 올라가는 명령어rmdir : 비어 있는 폴더(디렉토리)를 제거하는 명령어프로그램 설치이제 EC2에 접속했으니 git, java, mysql을 설치해보자. 먼저 아래와 같이 리눅스 터미널에 명령어를 입력하자.sudo yum update위의 명령어의 sudo는 관리자 권한으로 실행한다는 의미이고 yum은 리눅스 패키지 관리 프로그램 (gradle과 비슷한 역할)이다. update는 현재 설치된 여러 프로그램들을 최신화한다는 의미이다.깃 설치sudo yum install gitJDK11 설치sudo yum install java-11-amazon-corretto -ymysqlsudo yum install mysql-community-server // 설치 sudo systemctl status mysqld // 현재 보이지 않는 프로그램을 관리하는 명령어 + mysql 상태 확인 sudo systemctl restart mysql // mysql 재시작 sudo cat /var/log/mysqld.log | grep “A temporary password” // mysql 임시 비밀번호 확인 mysql –u root –p // mysql 접속빌드와 실행git clone 명령어로 우리가 깃헙에 올린 프로젝트를 가져오자.git clone [github 저장소 주소]이제 빌드준비를 위해 gradlew의 권한을 변경하자chmod +x ./gradlew이제 빌드를 하자 (단, 테스트는 제외)./gradlew build –x test그럼 jar파일이 생겼을텐데, 아래와 같은 명령어로 실행시킨다.java –jar build/libs/library-app-0.0.1-SNAPSHOT.jar --spring.profiles.active=dev그럼 서버가 정상적으로 실행된다. 다음으로 서버를 중단해보자. ctrl + c를 누르면 중단된다.하지만 우리는 터미널을 닫아도 서버는 계속 실행되고 싶다. 즉, 백그라운드 재생을 하고 싶어한다. 아래와 같이 입력한다.nohup [명령어] &그러면 백그라운드로 재생된 우리의 프로그램을 어떻게 종료할까?ps aux | grep java위와 같이 현재 실행중인 프로그램 중 java가 들어가는 프로그램을 확인해서 pid값을 알아내 아래와 같이 입력한다.kill -9 프로그램번호또한 파일의 내용을 확인해 볼 수 있는 명령어도 알아보자.vi : 리눅스 편집기인 vim을 사용하여 파일을 연다.또한 vi말고도 cat 명령어도 있다.cat : 파일에 있는 내용물을 모두 출력하는 명령어또한 끝부분만 확인하고 싶을때 아래와 같이 입력한다.tail : 현재 파일의 끝 부분을 출력하는 명령어여기서 실시간으로 확인하고 싶을 경우 f옵션만 주면 된다.가비아를 이용한 도메인 구입가비아를 통해 도메인을 구입해보았다. 실제 과정은 매우 단순함으로 생략한다.Day13. SpringBoot 설정 및 버전업여기서는 내용을 조금 축약해서 작성해보겠다. 이전에 배웠던 개념이기도 하고 중요하지만 실습적인 부분은 아니기에 간략히 작성한다. 우리는 여기서 gradle의 구성에 대해 알아보았다. 이 gradle 파일안에는 플러그인 설정, 의존성 설정, 저장소 설정등을 확인할 수 있었다. 또한 스프링이 어떻게 생겨났는지 스프링부트는 또 어떻게 생겨났는지 이 둘의 차이는 무엇인지 알 수 있었다. 그리고 yaml 문법과 properties 문법에 대해 알아보았다. 다음 우리의 프로젝트에서 롬복을 적용해서 리팩토링과정도 알아보았다. 마지막으로 스프링부트 버전을 3.x로 바꾸어보았다. gradle에서 스프링부트 버전을 변경하고 빌드할때 달라진 부분들을 고쳐주는 작업을 해보았다.Day14. 마무리 및 꿀팁우리는 여기서 앞으로의 공부 방향성, AWS 비용계산방법, myBatis 적용, 정적파일 처리방법을 배웠다. 나는 여기서 느꼈던 점은 공부 방향성에 있어서 코치님 말씀대로 코틀린 및 스프링의 다양한 모듈에 대해 접근해볼 예정이다.또한 이 수업에서 myBatis를 적용해보았는데 개인적으로 내 스프링부트 버전이 myBatis starter 몇 버전을 쓰는지 복잡함을 느꼈다. 또한 여전히 문자열로 쿼리를 작성한다는게 나한테에 있어서 많은 불편함을 느꼈다. 하지만 여기서 코치님은 대용량 데이터를 insert할때 jdbcTemplate을 이용한 batch 쿼리 실습을 해주셨는데 시간차이를 보니 완전 신세계였다. 이 부분에 대해 좀 더 자세히 알아봐야겠다.미니 프로젝트나는 미니프로젝트를 개발해가면서 많은 어려움과 좌절을 맛 보았다. 1단계는 나름 간단해서 별거 없네라는 식으로 넘어갔다. 하지만 다른 러너분들과 리뷰과정에서 많이 고쳐야 할 점을 보았다. 또한 단계가 갈수록 코드가 점점 개판이 되어간다는 나 자신이 너무 싫었고 특히 마지막 단계는 밤을 꼬박 새워서 해결할 수 있었다. 공공데이터포털로 법정공휴일 api를 가져오려 했지만 이 api가 몇번 타임아웃이 발생한다. 이 문제때문에 시간을 쏟은것은 안 비밀!리뷰중에는 왜 이렇게 작성했냐부터 이렇게 바꾸는 것이 어떤가의 대해 의문점을 던져주셨고 이것을 깊이 통찰하는 시간이 나를 성장하는 계기를 만든 것 같다. 자세한 개발과정은 1단계와 똑같은 절차로 해결했으니 1단계 개발일지를 참조해보시면 좋을 것 같습니다. 개발일지https://inf.run/rF31s PR1단계: https://github.com/crispindeity/warming-up-study-mini/pull/82단계: https://github.com/crispindeity/warming-up-study-mini/pull/93단계: https://github.com/crispindeity/warming-up-study-mini/pull/134단계: https://github.com/crispindeity/warming-up-study-mini/pull/15 최종 머지한 내 프로젝트https://github.com/SungbinYang/warming-up-study-mini/tree/main/sungbin/mini회고드디어 스터디클럽의 여정이 끝났다. 정말 힘들고 출사표때 전달했던 많이 부딧혔고 깨졌다. 그러면서 나는 점점 성장을 해 나간것 같다. 비록 1달이라는 짧다면 짧은 여정이였지만 내 학습의 여정은 아직 끝이 안 났기에 계속 달려볼려고 한다. 이 클럽을 수료하더라도 혹은 1기로 다시 재 신청을 하더라도 내 본연의 학습여정은 계속 될 것이며 그 여정동안 많이 깨지고 부딪히면서 점점 성장하는 개발자 양성빈이 되어야겠다. 화이팅 🔥🔥🔥🔥 📚 참조http://www.jjal.today/bbs/board.php?bo_table=gallery&wr_id=94&sfl=wr_subject%7C%7Cwr_content%7C%7Cwr_4&stx=웃짤&sop=and&page=7

백엔드인프런워밍업스터디클럽발자국마지막

양성빈(Robert)

[인프런 워밍업 스터디 클럽] 출사표 및 OT 후기

출사표 및 OT 후기 출사표현재 월드 스타인 손흥민 선수의 아버지인 손웅정 선생님께서 하신 말씀으로 글을 시작하려고 합니다.손웅정 선생님께서는 아이들을 가르치실 때 이런 말씀을 하셨습니다. "세계의 벽 절대 안 높아! 할수 있어! 남자는 자신감! 일단 붙어봐야될거아니야! 저질러보고! 깨지고! 박고! 가슴만 뛰는게 축구선수가 아니라 가슴이랑 내가 같이뛰어야돼!!!" 나는 현재 작은 중소기업의 웹 프론트엔드 업무를 맡고 있는 개발자입니다. 하지만 저의 원래 꿈은 웹 백엔드 개발자였습니다. 대학교 교수님 추천으로 이 회사에 입사하게 되었고 백엔드 업무를 맡기신다고 말씀하여 입사확정을 받았지만 정작 백엔드 팀은 존재하지 않았고 프론트 업무를 맡게 되었습니다. 이에 백엔드로 이직을 염두해두며 업무시간에는 프론트 기술들을 프로젝트에 적용하며 프론트 기술 적응을 해두었으며 집에 와서는 백엔드 공부를 계속 하였습니다. 그게 어느덧 3년이라는 시간이 흐르고 점점 제 자신에게 지칠 무렵, 인프런 배너에서 엄청난 것을 보게 되었습니다! 😲 워밍업 클럽이라는 말에 신청을 바로 하려 하였지만 상세소개 글에 걸리는 부분이 있었습니다. 🧐 '부트 캠프 참가자'라는 말에 이것을 신청해도 되는지 엄청 고민을 하였습니다. 그런던 어느날 '개발바닥' 유튜브 라방 을 보게 되었다. 마침 향로님이 계셔서 조심스레 여쭤보았고 직장인도 신청해도 된다는 말씀을 남겨주셨습니다! 나는 환호성을 외쳤고 바로 신청을 하게 되었습니다. 😆 서두에 손웅정 선생님 말씀처럼 조금 나태해진 저를 '인프런 워밍업 클럽 스터디'를 통하여 한번 저질러 보고 미션이나 강의를 들으면서 한번 깨저도 보고 가슴만 뛰는게 아닌 가슴과 제 학습곡선이 같이 뛰었으면 하는 바램으로 열심히 해보겠습니다! OT 후기전 날 '인프런 워밍업 스터디 클럽'의 커뮤니티를 가입하게 되었고 당일 온라인 라이브로 OT를 진행하게 되었습니다. 간단한 일정과 방법을 코치님이 말씀을 주셨고 간단한 자바의 역사에 대해 알려주시고 간단한 질문을 받은 뒤, 라이브는 종료되었습니다. 여기서 나는 일정이 빡빡하다는 것을 알고 평일에는 직장에 소모되다 보니 과제를 미리미리 해보자는 마음을 갖게 되었고 오늘 OT 들은 자바부터 상세히 파보기 시작했습니다. 그러면서 여러 자료를 찾다가 다른 러너분들과 공유되고 싶다는 글을 찾게 되었습니다. 이렇게 첫 시작으로 지금 마음가짐으로 끝까지 완주해서 우수러너까지 노려봤으면 좋겠습니다. 다들 응원 부탁드립니다. 🥳 📚 참고자료오라클 블로그 

백엔드인프런워밍업스터디클럽출사표발자국

이효정

[인프런 워밍업 클럽: 쿠버네티스] 4주차 발자국

쿠버네티스에 대해 1도 몰랐던 내가,불과 4주 만에 이렇게 성장할 줄 누가 알았을까?  처음 인프런에서 '인프런 워밍업 클럽 4기’ 모집 메일을 받았을 때가장 눈에 들어온 건 ‘데브옵스’라는 단어였다.백엔드, CS, 데브옵스... 다양한 분야가 있었지만나는 왠지 모르게 데브옵스가 궁금했고, 직접 해보고 싶었다.도대체 데브옵스가 뭔데 이렇게 자주 나오는 걸까?  사실 나는 취준생이고, 인프라나 시스템 쪽은 낯설기만 했다.공고를 보다 보면 ‘쿠버네티스’, ‘도커’ 같은 단어들이 눈에 들어오는데,보는 순간 겁부터 났다. ‘나는 저런 거 몰라... 어렵겠다...’그런 생각이 계속 들었다.  역시 이럴때! 직접 해봐야 안다.4주 전만 해도 쿠버네티스에 쫄아 있던 내가,지금은 쿠버네티스, 도커, 데브옵스 보면왠지 친근하고 반갑다. ㅎㅎㅎ 이 정도면 대성공 아닌가?!    인프런 워밍업 클럽 4기 참여와 ‘쿠버네티스 어나더 클래스’ sprint1, sprint2를 통해나는 쿠버네티스를 단순히 ‘해봤다’에서 끝나는 게 아니라,‘계속 공부하고 싶다’, ‘더 알고 싶다’는 마음이 생겼다. 그리고 새로운 목표도 생겼다.나도 일프로님처럼 멋진 지식공유자가 되고 싶다!그러기 위해서:아는 것을 정리하고, 기록해서 내 것으로 만들기새로운 기술 앞에서 주저하지 않고 먼저 도전하기 비록 지금은 대기업에 다니는 것도, 엄청난 커리어를 가진 것도 아니지만,현실에 주저앉지 않고, ‘나도 1%의 삶을 누릴 수 있다’는 믿음을 가지고 나아가려고 한다.  나는 할 수 있다. 그리고, 끝까지 해볼 거다.앞으로도 나의 여정은 계속된다. 🔥🔥🔥

데브옵스 · 인프라인프런발자국끝까지화이팅

이효정

[인프런 워밍업 클럽: 쿠버네티스] 3주차 발자국

데브옵스 한방 정리다른 내용도 있었지만 가장 흥미롭게 봤던 부분을 요약했다!GitOps: 데브옵스 파이프라인을 Git 하나로 통일/ 이슈, 협업관리,빌드, 테스트, 배포DevSecOps: 보안적인 요소까지 고려 및 자동화, 취약점 검사 체크MLOps: 머신너링 상품추천 사용자 행동 예측LLMOps: GPT 처럼 방대한 규모 머신너링에 특화덴 데브옵스 파이라인 필요FinOps: 클라우드 환경 비용 절감 포커스 손쉽게 데브옵스 환경을 구축하는 방법 배포를 시작하기 전에 반드시 알아야 할 것들강의도 듣고 미션5도 열심히 진행했다~미션5 진행 과정을 잘 작성해서 인프런 블로그에 남겨야겠다. 이번 미션5는 같은 이미지를 도커에서 받았을 때와 쿠버네티스에서 받았을 때 사이즈가 다른 이유와 Docker와 Containerd 명령 실습을 했다. 이번 실습에서 기억남은 것은 같은 이미지를 도커, 쿠버네티스에서 받았을 때 사이즈가 다른 이유와 관련된 실습이다. 도커는 "이미지 재구성"을 한다. 도커는 단순히 이미지를 다운로드하는 데서 끝나는 게 아니라, 자신의 포맷(도커 이미지 형식)에 맞게 레이어를 재정렬하고 필요한 메타데이터나 manifest 정보를 추가해서 재구성한다. 그래서 도커에서 보면: 원래 이미지 용량: 248MB → 도커에선: 490MB 이런 식으로 사이즈가 커진 이미지가 생긴다.이러한 사실을 바탕으로 나중에 혹시나.. 인터넷이 연결되지 않는 환경에서는 이미지들을 다운 받아서 파일 형태로 복사해야한다면 이때 쿠버네티스에서 컨테이너를 사용한다고 Docker로 받은 이미지를 복사해 넣을 경우 불필요하게 이미지 사이즈가 커지게 되는 상황을 만들지 않도록 주의해야한다. 꼭 명심하기! Jenkins Pipeline (기초부터 Blue/Green 까지) Helm과 Kustomize 비교하며 사용-1 회고벌써 4주차라니, 시간이 정말 빠르게 지나간다. 쿠버네티스랑 조금씩 친해지고 있는 느낌도 들고 ^^하하....실습하면서 “이건 왜 안 되지?”, “저건 왜 이러지?” 싶은 순간들이 아직 많다. 강의 들어도 한 번에 딱 이해되는 건 아니지만 신기하게도, 복습을 위해 다시 강의를 들으면 “아~ 그 말이었구나” 하고 이해되는 순간이 오기도 해서 재밌다.블로그를 쓰다 보면 “아, 이런 과정을 거쳤었구나” 하면서 다시 한 번 정리되고, 여러 감정이 스쳐 지나간다.매일 강의 하나씩 듣고 실습하고 복습하는 루틴을 이어왔을 뿐인데, 벌써 15개 넘는 블로그 글이 쌓였다는 게 뿌듯하다.마지막 미션도 열심히 해보려고 한다. 아직 1주차 복습 글을 쓰지 못했는데, 조만간 꼭 정리해서 마무리해야겠다.

데브옵스 · 인프라인프런쿠버네티스3주차미션복습발자국

leeebug

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

인프런 워밍업 클럽 스터디에 참여하고 벌써 2주 차도 마무리에 접어들고 있다. 4주간의 스터디이기 때문에 생각보다 일정이 타이트하여 시간관리가 무엇보다도 중요한 시기라고 생각한다.이번 주에는 파일 업로드 기능을 구현해야하기에 1주 차 과제를 급하게 마무리하고 지난주 토요일부터 서둘어서 미리 학습을 시작했다.깃 레포지토리의 경우에는 지난주에 사용했던 템플릿을 거의 수정없이 그대로 사용해서 개발환경 구축은 크게 어렵지 않았다.이번 주 강의에서는 Supabase Storage를 사용했는데 API가 잘 준비되어있어서 사용 방법을 익히는데도 크게 어렵지는 않았다. 다만 아래에서도 언급하겠지만 Supabase Storage는 AWS S3 기반으로 구현되어 강력한 네이밍 규칙이 적용되어 개인적으로는 Supabase Storage와 Supabase DB를 함께 사용했다. 📝 2주차 학습Supabase Storage클라우드 기반 객체 저장소로, AWS S3와 유사한 방식으로 파일을 저장하고 관리하는 서비스파일 및 이미지 업로드 및 관리 기능 제공PostgreSQL과 연동 가능권한 관리(RLS) 및 퍼블릭/프라이빗 파일 설정 가능Supabase SDK 또는 Restful API로 사용 가능  ✔ 파일명 규칙 (Supabase & AWS 공통) ASCII 문자, 숫자, 일부 특수 문자 허용 (- _ . /) 파일명을 /로 구분하여 폴더처럼 사용 가능 (folder/image.png) 공백 포함 가능하지만, URL Encoding이 필요할 수 있음 파일명에 한글, 이모지, 특수문자가 포함될 경우 정상적으로 업로드되지 않을 가능성이 있음 → URL-safe 변환 권장 React DropzoneReact에서 간편하게 파일 Drag & Drop 기능을 구현할 수 있는 라이브러리HTML5 File API를 활용하여 파일 업로드를 쉽게 구현할 수 있는 기능을 제공파일 타입, 크기, 개수 등 다양한 제약 조건 설정 가능비동기로 파일을 처리할 수 있는 onDrop 이벤트 제공 📋 2주차 미션💬 GitHub 저장소🚀 데모 영상 보러가기미션 해결 과정 요약2주 차 미션은 Next.js, React Query, TailwindCSS를 사용하여 이미지 업로드 앱을 구현하기였다. 필수 구현 기능으로는 이미지 업로드 기능(클릭 업로드 방식과 Drag & Drop 방식, 다중 업로드)과 이미지 삭제와 이미지 검색 기능 구현하기였다. 추가 기능은 파일의 마지막 수정 시간을 화면에 출력하는 UI 구현하기였다. 여기에 과제의 완성도를 높이기 위해서 개인적인 챌린지로 파일명에 한글 또는 특수문자 포함된 파일 업로드 기능, 1MB 미만으로 이미지를 압축하는 기능, 다운로드 기능을 추가로 구현하였다. 과제 추가 구현 기능✅ 마지막 수정 시간 표시const { error: insertError } = await supabase.from(DB_TABLE_NAME).insert({ name: file.name, originalName: originalFileName, imageId: uploadedFile.id, imageUrl: publicUrl, createdAt: new Date(file.lastModified).toISOString(), })생성: DB에 파일 데이터 업로드 시 createdAt에 file.lastModified를 ISOString 형식으로 저장 if (dbData) { const { error: updateError } = await supabase .from(DB_TABLE_NAME) .update({ name: file.name, originalName: originalFileName, imageUrl: publicUrl, updatedAt: new Date().toISOString(), }) .eq('imageId', uploadedFile.id)수정: DB에 해당 ID가 존재할 경우 updatedAt(string | null)에 현재 시간을 ISOString 형식으로 저장// DropImageManager 컴포넌트에서 생성 시간, 수정 시간을 포멧팅하여 DropImage 컴포넌트에 프롭스로 전달 const localCreatedAt = getLocalTime(image.createdAt) const localUpdatedAt = image.updatedAt ? getLocalTime(image.updatedAt) : null <!-- JSX 정렬이 잘 안되서 렌더링 형태만 봐주세요! --> <div className="w-5/6 truncate"> <span className={`text-[0.7rem] font-semibold ${localUpdatedAt ? 'text-mint-800' : 'text-gray-500'}`} > {localUpdatedAt ? localUpdatedAt : localCreatedAt} </span> {localUpdatedAt && ( <span className="text-[0.7rem] font-semibold text-mint-800"> (수정)</span> )} </div>출력: updatedAt이 존재할 경우 updatedAt과 (수정) 을 함께 출력, updatedAt: null이라면 createdAt를 출력 개인 챌린지 기능✅ 파일명 자동 변환 후 이미지 업로드하는 기능을 구현 (UX 개선)파일명 검증: 정규식을 활용하여 한글 및 특수 문자 포함 여부를 확인자동 변환: 검증 후 8자리 랜덤 문자열로 안전한 파일명 생성업로드 처리: 변환된 파일명으로 File 객체 생성 후 formData.append로 원본 파일명 함께 전송서버 액션: Supabase Storage에 저장 후, 완료 시 DB에 메타데이터 저장하여 연동결론: 파일명 변환을 자동화하여 업로드 오류를 방지하고, 원본 파일명도 유지하여 검색 및 관리 UX 개선✅ 파일 용량이 1MB 초과 시 자동 압축 후 업로드하는 기능을 구현 (UX 개선)browser-image-compression 라이브러리를 사용하여 파일의 용량 검증 후 1MB 초과 시 이미지 압축 후 업로드결론: 이미지 최적화로 업로드 속도 향상, 스토리지 비용 절감 효과✅ Blob URL을 활용한 다운로드 기능 추가 (UX 개선)Blob URL 생성: 업로드된 이미지를 fetch()로 가져와 Blob으로 변환다운로드 기능 구현: window.URL.createObjectURL(blob)으로 브라우저에서 직접 다운로드 가능하도록 처리결론: Blob URL 다운로드 방식을 적용하여 최적화된 이미지를 빠르게 다운로드 받을 수 있도록 개선🚧 기능 구현 시 어려웠던 부분Supabase Storage에 전달하는 File 객체 커스텀 불가원본 파일명을 추가하려 했으나, File 객체 자체를 수정하는 것이 제한적이다.파일 객체를 복사하여 원본 파일명을 추가하는 방법 시도전개 연산자를 사용하여 객체 복사 후 원본 파일명을 추가하려 시도하였으나 file 객체는 일반적인 방법으로는 복사할 수 없는 특별한 객체이다.ExtendedFile 확장 클래스로 인스턴스를 생성했으나 서버에 전달되지 않는 문제 발생확장된 ExtendedFile 객체를 formData에 담아 서버로 전송했지만, 서버에 정상적으로 전달되지 않았다.최종 해결 방법formData.append("file", file) formData.append("originalFileName, file.name)file 객체와 원본 파일명을 함께 서버로 전송 후 가공하여 Supabase Storage의 파일명에는 안전한 파일명만 저장하고 DB에 스토리지Id, 원본 파일명, 안전한 파일명, 이미지URL 등 정보를 저장했다. 🧾 ERD 다이어그램👀 2주차 회고아직 갈 길이 멀지만, 리팩토링을 통해 Next.js의 장점을 살릴 수 있는 구조로 점점 개선되어가는 과정을 경험하면서 이번 주 역시 알차게 보냈다고 생각한다.이번 주는 특히 MVP 패턴과 비슷한 형태로 컴포넌트 구조를 잡는 것에 익숙해지는 것을 개인적인 목표로 삼았다. 처음부터 MVP 패턴을 염두해 두고 설계한 것은 아니었지만, 진행하다보니 자연스럽게 MVP와 유사한 패턴으로 정리되어 가는 것을 느꼈다.화면 렌더링 시 상호작용이 필요하지 않은 정적인 요소들까지 클라이언트 컴포넌트로 관리하면 불필요한 하이드레이션 부담이 증가할 수 있다는 점을 다시 한번 체감했다.클라이언트 컴포넌트 내에서도 역할을 나눠 서비스 레이어나 상태 관리만 담당하는 매니져 컴포넌트와 프롭스로 상태를 전달받아 단순히 화면을 렌더링을 담당하는 UI 컴포넌트로 분리하는 연습을 진행했다.이러한 구조로 개선하면서 클라이언트 컴포넌트의 부담을 줄이고, 유지보수성을 높이는 방향으로 점차 최적화되고 있다는 점이 느껴졌다. 아직 개선해야 할 부분이 많지만 점진적으로 개선하여 더 나은 아키텍쳐를 만들어가는 과정이 의미 있었다고 생각한다.

풀스택워밍업클럽3기발자국회고과제미션

f1rstf1y9

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

2주차 학습 내용운영체제CPU 스케줄링 알고리즘 - SJF(Shortest Job First)Burst Time이 짧은 프로세스를 먼저 실행하는 알고리즘이론적으론 FIFO보다 성능이 좋지만, 구현에 문제가 발생할 수 있음어떤 프로세스가 얼마나 실행될지 예측 어려움Burst Time이 긴 프로세스는 아주 오랫동안 실행되지 않을 수도 있음CPU 스케줄링 알고리즘 - Round Robin한 프로세스에게 일정 시간만큼 CPU를 할당하고, 할당 시간이 지나면 강제로 다른 프로세스에게 일정 시만큼 CPU를 할당하는 알고리즘프로세스에게 할당하는 일정 시간 => 타임 슬라이스, 타임 퀀텀타임 슬라이스의 값에 따라 RR 알고리즘의 성능이 크게 바뀜타임 슬라이스가 큰 경우(무한대라고 가정)먼저 들어온 프로세스의 작업이 종료될 때까지 실행 => FIFO 알고리즘이 됨타임 슬라이스가 작은 경우(1ms라고 가정)컨텍스트 스위칭이 너무 자주 일어남프로세스 처리량보다 컨텍스트 스위칭을 처리하는 양이 더 커짐 => 오버헤드가 너무 큼최적의 타임 슬라이스 : 사용자가 느끼기에 여러 프로세스가 버벅거리지 않고, 동시에 실행되는 것처럼 느껴지면서 오버헤드가 너무 크지 않는 값Windows는 20ms, Unix는 100ms 정도 CPU 스케줄링 알고리즘 - MLFQ(Multi Level Feedback Queue)오늘날 운영체제에서 가장 일반적으로 쓰이는 CPU 스케줄링 기법기본적으로 CPU 사용률과 I/O 사용률이 좋게 나오는 작은 크기의 타임 슬라이스를 선택하되, CPU Bound Process들에게는 타임 슬라이스를 크게 줌CPU를 사용하는 프로세스가 스스로 CPU를 반납하면 I/O Bound Process일 확률이 높고, 실행 도중 CPU 스케줄러에 의해 강제로 CPU를 뺏기는 상황이 발생하면 CPU Bound Process일 확률이 높음우선순위 큐를 여러 개 준비하여, 우선순위가 높으면 타임 슬라이스 크기가 작고, 우선순위가 낮으면 타임 슬라이스 크기가 커짐처음엔 타임 슬라이스가 작게 할당되어있다가, CPU가 강제로 뺏기면 좀 더 큰 타임 슬라이스를 할당받게 됨프로세스 간 통신프로세스는 독립적으로 실행되기도 하지만, 다른 프로세스와 데이터를 주고받으며 통신을 하는 경우도 있음한 컴퓨터 내에서 프로세스 간 통신하는 방법파일을 이용하는 방법통신하려는 프로세스들이 하나의 파일을 이용해 읽고 쓰는 방법파이프를 이용하는 방법운영체제가 생성한 파이프를 이용해 데이터를 읽고 쓰는 방법한 프로세스 내에서 쓰레드 간 통신하는 방법데이터 영역의 전역 변수나 힙을 이용해 통신네트워크를 이용한 방법운영체제가 제공하는 소켓통신이나 다른 컴퓨터에 있는 함수를 호출하는 RPC(원격 프로시저 호출)를 이용해 통신공유자원과 임계구역공유자원프로세스가 통신할 때 공동으로 이용하는 변수, 파일 등여러 프로세스가 공유하고 있기 때문에 각 프로세스의 접근 순서에 따라 결과가 달라질 수 있음임계 구역(Critical Section)여러 프로세스가 동시에 사용하면 안되는 영역경쟁 조건(Race Condition) : 공유 자원을 서로 사용하기 위해 경쟁하는 것임계 구역 문제를 해결하기 위한 조건상호 배제(Mutual Exclusion) 메커니즘 필요임계 구역엔 주어진 시간에 항상 하나의 프로세스만 접근할 수 있어야 한다동시에 여러 개의 요청이 있더라도 하나의 프로세스의 접근만 허용한다.임계 구역에 들어간 프로세스는 빠르게 나와야 한다.세마포어프로세스들은 대기 큐에서 공유 자원을 사용하기 위해 대기하고, 운영 체제가 관리하는 열쇠를 가진 프로세스만 공유 자원을 사용할 수 있음 => 이때 이 열쇠를 세마포어(정수형 변수)라고 함공유 자원의 개수에 따라 세마포어의 값 또한 달라짐wait() : 열쇠를 요청해서 열쇠를 받고 문을 잠금. 열쇠가 없으면 기다림signal() : 방에서 나와 문지키는 직원에게 열쇠 반납단점 : wait() 함수, signal() 함수의 순서를 이상하게 호출해서 세마포어를 잘 못 사용할 가능성이 있음 => 모니터로 해결!모니터운영체제가 처리하는 것이 아닌, 프로그래밍 언어 차원에서 지원하는 방법Java에서 synchronized라는 키워드가 붙으면, 이 키워드가 붙은 함수들은 동시에 여러 프로세스에서 실행시킬 수 없음 = 상호배제가 완벽하게 이루어짐만약 프로세스 A에서 increase() 함수를 실행하면, 프로세스 B는 synchronized 키워드가 붙은 decrease() 함수도 실행할 수 없음교착상태(데드락)여러 프로세스가 서로 다른 프로세스의 작업이 끝나길 기다리다가 아무도 작업을 진행하지 못하는 상태교착상태의 필요 조건 (한가지라도 충족하지 않으면 교착상태 발생 X)상호배제 : 어떤 프로세스가 한 리소스를 점유했다면, 그 리소스는 다른 프로세스에게 공유되면 안됨 비선점 : 프로세스 A가 리소스를 점유하고 있는데 프로세스 B가 리소스를 빼앗을 수 없어야 함 점유와 대기 : 어떤 프로세스가 리소스A를 가지고 있는 상태에서 리소스 B를 원하는 상태여야 함 원형 대기 : 점유와 대기를 하는 프로세스들의 관계가 원형을 이루고 있어야 함 교착 상태의 네가지 조건을 이용해서 교착상태를 예방하는 방법을 연구해보고자 했으나, 제약이 많고 비효율적이라 해결하는 방법을 연구함교착상태 회피(Deadlock avoidance)프로세스들에게 자원을 할당할 때 어느정도 자원을 할당해야 교착상태가 발생하는지 파악해서 교착상태가 발생하지 않는 수준의 자원 할당을 함전체 자원의 수와 할당된 자원의 수를 기준으로 안정 상태, 불안정 상태로 나눔운영체제는 최대한 안정 상태를 유지하려고 자원할당 함불안정 상태에 있더라도 무조건 교착 상태에 빠지는 것이 아닌, 교착 상태에 빠질 확률이 높아짐운영체제에서 은행원 알고리즘 구현하기안정상태은행원 알고리즘은 각 프로세스가 현재 요구할 수 있는 요청이 예상되는 자원을 구함  P1에서 4개를 요청하면 현재 사용 가능 자원이 2개뿐이므로 거절P2에서 2개를 요청하면 2개 모두 P2에 할당, P2는 작업을 마치고 6개를 반납사용 가능한 자원이 6개가 되어, P1, P3 모두에 필요한 만큼 할당 가능불안정 상태사용 가능한 자원이 1개현재 사용 가능한 자원 개수로는 P1, P2, P3가 요청할 수 있는 최대 요청인 2개를 충족하지 못함 => 불안정 상태모든 프로세스가 최대 자원을 요청하지 않는다면 교착 상태에 빠지지 않을 수도 있지만, 불안정상태에 빠지지 않도록 유지하는게 좋음교착상태의 검출 방식가벼운 교착 상태 검출프로세스가 일정 시간 동안 작업을 진행하지 않는다면 교착상태가 발생했다고 간주해결 방법 : 일정 시점마다 체크포인트를 만들어 작업을 저장하고 타임아웃으로 교착 상태가 발생했다면 마지막으로 저장했던 체크포인트로 롤백무거운 교착 상태 검출자원 할당 그래프 이용현재 운영체제에서 프로세스가 어떤 자원을 사용하는지 지켜보고 교착 상태가 발생했다면 해결그래프에 순환 구조가 생긴다면 교착 상태가 생긴 그래프교착 상태를 검출하면 교착 상태를 일으킨 프로세스를 강제 종료시키고, 다시 실행시킬 때 체크포인트로 롤백단점 : 운영체제가 지속적으로 자원할당그래프를 유지하고 검사해야하므로 오버헤드가 발생장점 : 가벼운 교착상태에서 발생하는 억울하게 종료되는 프로세스는 발생하지 않음메모리 종류레지스터가장 빠른 기억장소, CPU 내에 존재휘발성 메모리 - 컴퓨터의 전원이 꺼지면 데이터가 사라짐CPU를 구분할 때 말하는 32bit, 64bit는 레지스터의 크기를 말함CPU는 계산을 할 때, 메인메모리에 있는 값을 레지스터로 가져와 계산하고, 결과는 메인메모리에 저장캐시휘발성 메모리레지스터는 굉장히 빠르고, 메인메모리는 너무 느림 => 메인 메모리에 있는 값을 레지스터로 옮기려면 한참 걸리므로 필요할 것 같은 데이터를 미리 가져와서 캐시에 저장성능의 이유로 여러개를 둠메인메모리(RAM)실제 운영체제와 다른 프로세스들이 올라가는 공간전원이 공급되지 않으면 데이터가 지워지므로 휘발성 메모리하드디스크나 SSD보다 속도는 빠르지만, 가격이 비싸므로 데이터를 저장하기 보다는 실행 중인 프로그램만 올림 보조저장장치(HDD, SSD)사무용 프로그램, 게임, 작업한 파일 등을 저장할 필요가 있는데, 비싸고, 휘발성인 메모리에 저장할 순 없음가격이 저렴하고 전원이 공급되지 않아도 데이터가 지워지지 않는 비휘발성 메모리를 만듦 메인메모리오늘날의 컴퓨터 구조인 폰 노이만 구조는 모든 프로그램을 메모리에 올려 실행시킴운영체제는 메모리를 관리하기 위해 1바이트 크기로 구역을 나누고 숫자를 매김 => 이 숫자는 "주소"라고 부름물리 주소와 논리 주소물리 주소 : 메모리를 컴퓨터에 연결하면 0x0번지부터 시작하는 주소 공간논리 주소 : 사용자 관점에서 바라본 주소 공간사용자는 물리 주소를 몰라도 논리 주소로 물리 주소에 접근할 수 있음경계 레지스터메모리에는 수많은 프로세스가 올라오는데, 운영체제를 위한 공간은 따로 마련해둠. 이때,하드웨어적으로 운영체제 공간과 사용자 공간을 나누는 경계 레지스터를 만듦메모리 할당 방식유니 프로그래밍메모리 오버레이유니프로그램 방식에서 메모리보다 더 큰 프로그램을 실행시키는 방법큰 프로그램을 메모리에 올릴 수 있도록 잘라서 당장 실행시켜야 할 부분만 메모리에 올리고 나머지는 용량이 큰 하드디스크의 스왑 영역에 저장하는 기법멀티프로그래밍가변 분할 방식(세그멘테이션)프로세스의 크기에 따라 메모리를 나누는 방식장점 : 내부 단편화 없음단점 : 외부 단편화 발생고정 분할 방식(페이징)프로세스의 크기와 상관 없이 메모리를 정해진 크기로 나누는 방식 장점 : 구현이 간단하며 오버헤드가 적음단점 : 작은 프로세스도 큰 영역에 할당돼서 공간이 낭비되는 내부 단편화 발생버디 시스템오늘날 가변 분할 방식과 고정 분할 방식을 혼합하여 단점을 줄인 방식2의 승수로 메모리를 분할해 메모리를 할당하는 방식가변 분할 방식처럼 프로세스 크기에 따라 할당되는 메모리 크기가 달라지고, 외부 단편화를 방지하기 위해 메모리 공간을 확보하는 것이 간단함고정 분할 방식처럼 내부 단편화가 발생하긴 하지만 많은 낭비가 발생하진 않음알고리즘재귀(recursion)어떠한 것을 정의할 때 자기 자신을 참조하는 것재귀 함수탈출 조건이 없으면 콜스택이라는 메모리 공간이 가득차서 자동으로 종료언제 종료될지 예측할 수 있게 하기 위해 탈출 조건, 즉 기저 조건이 반드시 필콜스택함수가 호출되면서 올라가는 메모리 영역, 스택이라고도 부름FILO(먼저 들어온 데이터가 나중에 나감) 특성을 가짐함수를 호출하면 함수는 콜스택에 올라가고, 종료되면 콜스택에서 제거됨버블 정렬(Bubble Sort)앞에 있는 숫자와 뒤에 있는 숫자를 비교해서 자리를 바꾸는 알고리즘데이터를 옆 데이터와 비교하면서 자리를 바꾸는게 버블이 일어나는 것 같아서 붙여진 이름시간복잡도 : O(n^2)장점 : 이해와 구현이 쉽다단점 : 성능이 좋지 않다.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; } } } }선택 정렬 (Sellection Sort)배열의 정렬되지 않은 영역을 순회하며 가장 작은 값을 탐색 후 교체하는 방법시간복잡도 : O(n^2)장점 : 이해와 구현이 쉽다단점 : 성능이 좋지 않다function SelectionSort(arr){ for(let i = 0; i < arr.length - 1; i++){ let minValueIndex = i; for(let j = i + 1; j < arr.length; j++){ if(arr[j] < arr[minValueIndex]){ minValueIndex = j; } } let temp = arr[i]; arr[i] = arr[minValueIndex]; arr[minValueIndex] = temp; } }회고칭찬하고 싶은 점이번 주부터 알고리즘 스터디를 시작하고, 또 매일 출근도 하면서 오늘 있었던 코딩테스트까지 일주일 간 이것저것 할일이 많았는데 포기하지 않고 이번주차 진도를 따라잡았다. CS 중에서도 운영체제는 개념이 잘 잡혀있지 않았는데, 공부를 하면서 운영체제에서 가장 중요한 개념인 프로세스와 CPU 스케줄링에 대해 어느정도 개념을 이해할 수 있는 일주일이었다.아쉬웠던 점이번주에도 역시나 매일매일 정해진 분량을 학습하기보다는 시간이 남는 타임에 몰아들은 경우가 더 많았던 것 같다.보완해야할 점남은 일주일도 바쁠 예정이기 때문에..현실적으로 매일매일 정해진 분량을 지켜서 노트까지 정리해가며 학습하기엔 벅차더라도, 지난 주처럼 너무 한번에 몰아서 하지 말고 출퇴근 시간을 이용해서 강의를 가볍게 보고 추후에 다시 복습하는 개념으로 노트를 정리하면 좋을 것 같다.

워밍업클럽CS발자국

f1rstf1y9

[인프런 워밍업클럽 CS 2기] 1주차 발자국👣

1주차 학습 내용운영체제운영체제의 구조커널프로세스, 메모리, 저장 장치 등을 관리사용자가 직접 접근하지 못하고, 인터페이스로만 접근 가능인터페이스GUI(Graphic User Interface)GLI(Command Line Interface)시스템콜어플리케이션은 시스템콜을 활용해 커널에 접근드라이버하드웨어와 커널의 인터페이스 컴퓨터 하드웨어와 구조메인보드 : 다른 하드웨어를 연결하는 장치로, 장치 간 데이터 전송은 메인보드의 버스가 담당CPU(Central Processing Unit, 중앙 처리 장치)산술논리 연산 장치(ALU) : 실제 데이터 연산 담당제어 장치(Control Unit) : 모든 장치들의 동작을 지시, 제어레지스터: CPU 내에서 계산을 위해 임시로 보관메모리RAM(Random Acess Memory) : 전원을 끄면 데이터가 날아감, 메인 메모리로 사용, 랜덤한 위치의 어떤 데이터를 읽든 속도가 동일ROM(Read Only Memory) : 전원을 꺼도 데이터 보관 가능, 컴퓨터 부팅 관련 바이오스 저장부팅과정컴퓨터 전원 누름 -> ROM에 저장된 BIOS 실행 -> 주요 하드웨어 이상 여부 체크 -> 이상이 없으면 부트 로더 실행 -> 운영체제를 메모리로 불러오기 -> 바탕화면이 보임인터럽트CPU 입출력 명령이 들어왔을 때 언제 완료되는지 계속 확인해야하는 폴링 방식의 단점을 해결한 것CPU가 입출력 관리자에게 명령을 내리고 자기는 다른 작업함 -> 입출력이 완료되면 CPU에게 신호를 주고 CPU는 신호를 받아서 ISR 실행시켜 작업 완료 프로그램과 프로세스프로그램하드디스크와 같은 저장장치에 저장된 명령문의 집합저장 장치만 사용하므로 수동적인 존재프로세스하드디스크에 저장된 프로그램이 메모리에 올라갔을 때, 즉 실행 중인 프로그램메모리, CPU 사용 및 입/출력 등 능동적인 존재code, data, stack, heap 영역으로 구성멀티프로그래밍과 멀티프로세싱멀티프로그래밍 : 메모리에 여러 개의 프로세스가 올라온 것멀티프로세싱 : CPU가 여러 개의 프로세스를 처리하는 것PCB(Process Control Block)프로세스의 정보를 가지고 있는 것으로, 연결리스트 자료구조로 저장됨.포인터, 프로세스 상태, 프로세스 ID, 프로그램 카운터, 레지스터 정보, 메모리 관련 정보, CPU 스케줄링 정보 등을 저장프로세스 상태생성 -> 준비 -> 실행 -> *(대기 -> 준비 -> 실행 ->) 완료대기 상태: 입출력 요청이 들어오면 입출력이 완료될때까지 기다리는 상태컨텍스트 스위칭프로세스를 실행하는 중에 다른 프로세스를 실행하기 위해 실행중인 프로세스의 상태를 저장하고 다른 프로세스의 상태값으로 교체하는 작업컨텍스트 스위칭이 일어날 때 PCB의 내용이 변경됨쓰레드요구하는 작업의 수가 늘어나면 프로세스의 수가 늘어나고, 프로세스의 수만큼 PCB, 코드, 데이터, 스택, 힙 영역을 만들어줘야해서 무거워짐 => 이를 해결하기 위해 쓰레드 고안프로세스 내에 1개 이상 존재하며, PCB, 코드, 데이터, 힙 영역을 공유함CPU 스케줄링운영체제가 모든 프로세스에게 CPU를 할당하거나 해제하는 작업어떤 프로세스에게 CPU 리소스를 줄지, 얼마의 시간동안 CPU를 할당할지를 고려함Burst : 한 작업을 연속적으로 처리하는 것으로, 보통 작업 처리에 필요한 시간을 의미 (CPU Burst, I/O Burst)스케줄링 목표리소스 사용률 높이기, 오버헤드 최소화, 공평성, 처리량, 대기 시간, 응답 시간서로 상반되는 상황이 있기 때문에 사용하는 시스템에 따라서 목표를 다르게 설정CPU 스케줄링 알고리즘 - FIFOFirst In First Out, 먼저 들어온 작업이 먼저 나간다프로세스의 Burst Time에 따라 성능차이가 심하게 나므로 현대 운영체제에서 잘 쓰이지 않고 일괄처리 시스템에서 쓰임알고리즘자료구조와 알고리즘자료구조 : 데이터가 어떤 구조로 저장되고 어떻게 사용되는지를 나타냄알고리즘 : 어떤 문제를 해결하기 위한 확실한 방법자료구조에 따라 알고리즘이 달라지므로, 상황에 맞는 적절한 자료구조를 선택하고 이에 맞는 알고리즘을 적용할 줄 알아야함시간복잡도최선 : 빅-오메가, 평균 : 빅-세타, 최악 : 빅-오주로 빅-오 표기법을 많이 사용함성능 : O(1) < O(logn) < O(n) < O(nlogn) < O(n^2) < O(2^n) < O(n!)표기법계산에 가장 많은 영향을 미치는 항만 표기상수는 큰 의미 없음배열일반적인 배열크기가 정해져있고, 정해진 크기만큼 연속적인 메모리 공간에 값을 할당운영체제는 가장 첫번째 주소만 기억함참조 성능은 O(1), 삭제, 삽입 성능은 좋지 않음자바스크립트의 배열 상황에 따라서 연속적, 불연속적으로 메모리 할당하는데 대부분 불연속적으로 할당(내부적으로 연결)장점읽기, 쓰기와 같은 참조에는 O(1)의 성능을 가짐단점크기 예측이 힘들기 때문에 메모리 낭비가 발생할 수 있음데이터의 삽입, 삭제가 비효율적임연결리스트배열의 단점을 해결하기 위해 저장하려는 데이터를 메모리 공간에 분산해 할당하고, 데이터를 서로 연결해주면 됨 => Node를 만들어 수행Node의 구조 : data를 담는 변수, 다음 노드를 가리키는 변수장점배열에서 초기 크기를 알아야하는 단점이 없음중간에 데이터를 삽입 혹은 삭제하려면, 특정 노드의 다음 가리키는 노드만 바꿔주면 됨단점데이터들이 전부 떨어져있기 때문에 바로 접근하기 힘듦 => O(n)의 성능스택(Stack)First In Last Out(FILO)으로, 먼저 들어간 데이터가 가장 나중에 나오는 규칙이 있음Redo, Undo 기능, 괄호짝 검사 등에 사용큐(Queue)First In First Out(FIFO)로, 먼저 들어간 데이터가 먼저 나오는 규칙이 있음계산대에 줄을 서는 경우, 맛집 대기줄 등 일렬로 기다리는 줄을 생각하면 됨덱(Deque)데이터의 삽입과 제거를 head와 tail 두 군데서 자유롭게 할 수 있는 자료구조해시테이블(Hash Table)해시(Hash), 맵(Map), 해시맵(HashMap), 딕셔너리(Dictionary)라고도 불림해시와 테이블이 합쳐진 자료구조테이블을 그냥 배열에 저장하면, 낭비되는 공간이 발생할 수 있음테이블의 기존 인덱스를 순차적인 인덱스로 만들기 위해 어떤 계산을 하는 함수를 해시라고 함key만 알면 value에 O(1)의 성능으로 읽기가 가능삽입, 삭제, 수정도 O(1)장점빠른 데이터 탐색, 삽입, 삭제를 할 수 있음단점공간의 효율성이 좋지 않음좋은 해시 함수의 구현이 필수적임셋(Set)데이터의 중복을 허용하지 않는 자료구조해시 테이블을 이용하므로 쉽게 구현 가능해시 테이블을 사용한다고 해서 Hash Set이라고도 불림value 값은 사용하지 않고 key만 사용해서 구현함(key가 데이터)회고칭찬하고 싶은 점강의를 허투루 듣지 않기 위해서 강의 노트를 작성하면서, 좀 더 궁금한 점이 생기면 의문점을 작성해두고 추가로 찾아보는 등 디테일한 이해를 위해 노력했다. 아쉬웠던 점휴일이나 주말 등 강의를 들을 시간이 넉넉한 때를 생각하면서 당일에 들어야하는 분량을 미루는 일이 잦았다.보완해야할 점퇴근 후 피곤하더라도 완전히 그날 강의를 안듣기 보다는, 한 강의라도 들으면서 꾸준히 강의를 수강하는 습관을 들이도록 노력하자!

워밍업클럽CS발자국

이양구

[인프런 워밍업 클럽 FE 0기] 미션8 - 디즈니 플러스 앱

🎞 Disney Plus APP GitHub 🎞 Disney Plus APP DemoRecord by ScreenToGif  개요인프런 워밍업 클럽 FE 0기의 여덟 번째 미션인 '디즈니 플러스 앱' 입니다. 따라하며 배우는 리액트 섹션 4~5(리액트로 Netflix 앱 만들기) 목표swiper 라이브러리 커스텀해보기react-oauth/google 로 구글 로그인 연동해보기 구현swiper 라이브러리 커스텀해보기// LoginPage import "swiper/css/effect-fade"; <Swiper modules={[Autoplay, EffectFade, Pagination, A11y]} autoplay={auto} effect={"fade"} pagination={{ clickable: true, }} loop={true} fadeEffect={{ crossFade: true }} slidesPerView={1} speed={2000} > {...} </Swiper> // Row.tsx import "swiper/css/mousewheel"; <Swiper modules={[Navigation, Pagination, Scrollbar, A11y, Mousewheel]} navigation pagination={{ clickable: true }} mousewheel speed={1000} spaceBetween={10} > {...} </Swiper> 2024년 3월 10일의 디즈니 플러스 메인 페이지를 그대로 옮겨보고자 swiper 라이브러리를 커스텀해봤다.로그인 페이지에서는 좌우로 넘기는 슬라이드가 아닌 fade-in-out의 슬라이드를 구현하기 위해 swiper에 EffectFade 모듈을 추가하고 fadeEffect 속성을 추가했다.이 fadeEffect가 제대로 작동하기 위해선 반드시 해당 이펙트의 css를 추가해야 한다.다른 모듈이나 컴포넌트를 추가할 때처럼 자동으로 추가되지 않으니 주의해야 한다. (이걸 몰라서 한참을 찾았다. 😥)Row 컴포넌트는 마우스 휠에 따라 움직이는 슬라이드를 만들기 위해 Mousewheel 모듈과 속성을 이용했다.이렇게 슬라이드 속성을 정한 뒤에 swiper가 렌더링하는 요소의 class를 찾아 CSS에서 원하는 디자인으로 변경하면 된다.이때 라이브러리의 CSS와 겹치는 속성이 있을 수 있기 떄문에 '!important'를 붙이는 게 좋다. react-oauth/google 로 구글 로그인 연동해보기// index.js <GoogleOAuthProvider clientId={process.env.REACT_APP_CLIENT_ID}> <BrowserRouter> <App /> </BrowserRouter> </GoogleOAuthProvider> // App.jsx const navigate = useNavigate(); const [isLogin, setIsLogin] = useState( localStorage.getItem("user") ? true : false ); useEffect(() => { isLogin ? navigate("/") : navigate("/login"); }, [isLogin]); <Routes> {isLogin ? ( <Route path="/" element={<Layout setIsLogin={setIsLogin} />}> <Route index element={<MainPage />} /> <Route path=":movieId" element={<DetailPage />} /> <Route path="search" element={<SearchPage />} /> </Route> ) : ( <Route path="login" element={<LoginPage setIsLogin={setIsLogin} />} /> )} </Routes> react-oauth/google는 구글 로그인을 지원하는 라이브러리로, 사전에 구글의 Cloud에서 API 등록을 하고 Client ID를 발급받아야 사용할 수 있다.먼저 프로젝트의 최상위에 GoogleOAuthProvider로 감싸준다.그리고 사용자의 로그인 여부에 따라 페이지를 이동시키기 위해 라우터를 설정한 App 컴포넌트에서 관련 코드를 작성했다.페이지가 렌더링 될 때 로컬 스토리지에 저장된 유저 정보를 받아오고 만약 없다면 로그인 페이지로 보내도록 했다. // loginPage const googleLogin = async (credentialResponse) => { localStorage.setItem( "user", JSON.stringify(jwtDecode(credentialResponse.credential)) ); setIsLogin(true); }; <GoogleLogin onSuccess={(credentialResponse) => googleLogin(credentialResponse)} /> GoogleLogin 컴포넌트는 react-oauth/google 라이브러리에서 지원하는 버튼 컴포넌트로 디자인 및 로그인 관련 함수가 내장되어 있다.onSuccess는 사용자의 로그인이 성공했을 때 실행되는 콜백 함수이며, 인자로 로그인한 유저의 정보를 담은 데이터를 갖는다.여기서 credential이라는 값은 유저의 정보를 담고 있는 토큰으로 암호화되어 있기 때문에 jwt-decode 라이브러리를 이용해 디코딩하여 사용해야 한다.여기서 받은 picture는 사용자의 프로필 이미지 링크를 포함하고 있어서 Nav 컴포넌트에서 사용해 로그인한 유저의 프로필 이미지로 변경했다. 회고'Netflix 앱 만들기'를 하면서 사용했던 기술이 대부분이라 오래 걸리지 않을 것 같았지만...라이브러리 알아보고 문서 읽고 실행해보고... 하는 데 너무 오래 걸린 것 같다.배너 하단의 카테고리 부분은 이전에 같은 과제를 하셨던 분의 깃허브를 참고했다. (https://github.com/kimneighbor/clone-disney-plus-app)로그인 페이지는 따라하기 싫어서 현재 디즈니 플러스 홈페이지를 보고 참고했다.그대로 하면 얼마 안 걸릴 거라 생각했는데 생각보다 라이브러리 커스텀에서 좀 애를 먹었다. 😅with_networks: "2739" 2739는 TMDB에서 디즈니 플러스 방송사(networks) 코드라서 axios의 instance 기본 값에 추가했다.몇몇 요청은 해당 파라미터가 통하지 않거나 오류를 보내기도 해서 완벽하진 않다.디즈니 플러스에서 API를 제공했다면 더 알맞게 페이지를 구현할 수 있었을 텐데 하는 아쉬움이 남는다.한편 영화 정보 API를 제공해주는 TMDB(The Movie Database) 같은 곳이 있어 감사하고 다행이라는 생각이 들었다.프론트엔드 공부하는데 API를 제공해주는 곳이 아예 없었다면 혹은 매번 일정 비용을 지불해야 했다면 얼마나 힘들었을까로그인도 사실 좀 더 좋은 라우팅 구조나 상태 관리 라이브러리를 공부하고 사용해보고 싶었지만...계속 욕심만 커지는 것 같아 최대한 간단하게 구현하려 했다.(사실 과제 밀려서 조바심에 아무것도 못 했다... 😂) 

프론트엔드워밍업워밍업클럽프론트엔드프론트FE미션과제발자국

양성빈(Robert)

[인프런 워밍업 스터디 클럽] 0기 첫번째 발자국 (1 week)

발자국어느덧 인프런 워밍업 스터디 클럽을 시작한지도 1주째가 다 되간다. 부트캠프 수료자 대상으로 하는 인프런 워밍업 스터디 클럽을 이직 및 직무전환을 고려하는 저에게 기회를 주셔서 정말 최선을 다해 열심히 해서 완주는 물론이고 우수러너에도 들어볼 생각이다. 그럼 본격적으로 발자국을 작성하고 1주간 회고를 시작해보겠다.강의 요약Day0인프런 워밍업 스터디 클럽을 본격적으로 시작하기 전에 OT를 온라인으로 참석하게 되었다. 살짝 기대 반 불안감 반으로 시작을 하였다. OT에는 코치님이 인프런 워밍업 스터디 클럽에 대한 전반적인 일정과 미션 제출 방법, 완주러너, 우수러너 선정방법등 전반적인 일정과 규칙에 대해 설명해주셨다. 또한 남은 시간 동안 코치님은 자바의 역사에 대해서 설명해주셨다. OT의 짧은 특강 (feat. 자바의 역사)자바 도대체 어떤 역사를 갖고 있는가?자바가 처음 발표되고 많은 사람들에게 관심을 받았지만 자바7이 되던 시점, 자바의 암흑기가 잠깐 찾아오게 되었다. 그 이유는 그 당시에 nodejs라던지, python이라던지 이런 언어들이 관심을 받게 되었고, 자바는 너무 오래된 언어, 뭔가 부족하다라는 인식이 생기게 되었습니다.그리고 2014년쯤 자바8이 발표가 되었다. 자바8은 대격변의 버전이였다. 아주 중요한 역할을 했으며 자바의 안 좋은 인식들을 걷어내게 된 계기가 된 버전이였다. 간략하게 보면, 자바8에는 람다, @FunctionalInterface등이 등장하며 함수형 프로그래밍이 가능하게 되었다. 또한 Stream이 등장하며 Stream 연산이 가능하게 되었다. 그리고 우리가 요즘 많이 쓰는 Optional도 자바8에 등장하였다. 이 외에도 날짜, 시간을 다루는 방법이 확장되게 되었다. 자바의 이후 역사이후 자바는 현재 자바21까지 나왔으며 현재 LTS 버전은 21이다. 그리고 JDK17이후부터 LTS 버전 주기가 2년으로 변경되었다. 아래의 오라클 블로그를 참고하기 바란다. 📚 오라클 블로그https://blogs.oracle.com/java/post/moving-the-jdk-to-a-two-year-lts-cadence 이렇게 OT 라이브 세션이 등장하였고 직장인인 저에게 이런 기회를 주셔서 정말 감사했으며 이에 부응하게 더욱 열심히 해서 완주러너를 넘어서 우수러너가 되기를 목표로 삼으며, 이 스터디 클럽이 끝날 때 성장이라는 것을 했으면 하는 마음이다. 아래는 내가 미리 작성해둔 출사표에 관련한 블로그이므로, 참고바랍니다. 📚 나의 출사표https://inf.run/jR96P Section01일차가 시작되기 전에 Section0 강의를 미리 수강해두기로 하였다. 그에 대한 내용을 요약해보겠다. Java, IntelliJ, Postman, MySQL, git 설치처음에는 Java, IntelliJ, Postman, MySQL, git을 운영체제별로 설치하는 방법을 알려주셨다. 나는 이전에 설치가 되었기에 이 부분은 쉽게 수강을 할 수 있었다. 스프링 프로젝트를 시작하는 첫 번째 방법강좌에 강의자료를 다운로드 받으면 PPT와 소스코드 압축파일이 있다. 압축파일을 해제 후에 인텔리제이의 open을 눌러서 해당 소스코드를 열었다. 초기에는 많은 것들을 다운로드 하기 때문에 조금 기다려줘야 한다. 다운로드가 완료되면 LibraryAppApplication 클래스를 찾는다. (src/main/java/패키지명/LibraryAppApplication.java) 이후에 이 클래스를 실행시킨다.이 부분 또한 이전에 학습을 개인적으로 했던 부분이라 쉽게 수강을 하였다. Java를 공부하기 전에 알아두면 좋을 것들! #1 (JVM, JDK - 유튜브)자바라는 언어를 어떻게 컴퓨터가 알아먹을까?컴퓨터는 0과 1밖에 모르는 바보다. 그래서 코드를 알아먹지 못한다. 이를 위해 코드를 컴파일이라는 과정을 거쳐서 컴퓨터가 알아먹을 수 있는 바이트코드(0과 1로 된 코드)로 번역해줘야 한다.컴파일: 인간이 이해하기 쉬운 언어를 기계어(바이트 코드)로 바꾸는 과정컴파일러: 컴파일하는 프로그램바이트코드: 0과 1로 이루어진 코드, 컴퓨터가 이해할 수 있는 코드0과 1은 운영체제마다 다르다. C언어 같은 경우 각각 운영체제별 다른 컴파일러가 필요하다. 하지만 자바는 특별하다. 자바는 하나의 컴파일러로 똑같은 바이트코드를 만든다. 그 이후, 운영체제 별 JVM에게 전달하고 이 JVM이 또 번역해서 각 운영체제에게 전달해준다. 원래는 운영체제마다 다른 '컴파일러'가 필요하지만 자바는 JVM이 0과 1을 운영체제에 맞게 번역을 해준다. 이 JVM은 인기가 상당해서 자바외에도 다른 언어들에도 사용된다.(ex. kotlin, groovy...) JVM자바 가상머신운영체제별 존재바이너리 코드를 읽고 검증 및 실행JRE자바 실행환경JRE = JVM + 자바 프로그램 실행에 필요한 라이브러리JVM의 실행환경 구현JDK자바 개발도구JDK = JRE + 개발도구컴파일러, 디버그등이 포함JDK를 설치하는 행위는 JDK만 설치되는 것이 아니라 그 안에 포함한 JRE + JVM이 같이 설치되는 것이다.LTS 버전LTS버전이란 오래써도 되는 버전을 말한다.JDK 종류Oracle: 개인은 무료, 기업은 유료open JDK: oracle JDK와 비슷한 성능, 언제나 무료Java를 공부하기 전에 알아두면 좋을 것들! #2 (빌드, 빌드툴 - 유튜브)빌드소스코드 파일을 컴퓨터에서 실행할 수 있는 독립 소프트웨어 가공물(Artifact)로 변환시키는 과정즉, 소스코드 파일을 Artifact로 만드는 과정1-1. 빌드 과정소스코드 컴파일테스트코드 컴파일테스트코드 실행테스트코드 리포트기타 추가 설정한 작업들 진행패키징최종 소프트웨어 결과물을 만들어낸다.🙋🏻 테스트코드란?내가 작성한 코드를 자동 테스트해주는 코드를 추가로 작성하는 코드실행내가 작성한 코드 (혹은 테스트 코드)를 컴파일을 거쳐 작동시켜 보는 것Artifact가 나올 수도 있고 안 나올 수도 있다.⚠ 주의인터프리터 언어는 컴파일이 필요 없다. 인터프리터의 대표적인 언어로는 파이썬, 자바스크립트가 있다.그런데 이런 빌드 과정이 이렇게 긴데 이것을 사람이 수동으로 하면 무조건 실수가 나오기 마련이다. 내가 생각해도 그럴 것이다. 현재 회사에선, 이런 과정을 일일이 한 경험이 있기 때문이다. 이런 경험 기반으로 간절했던 마음은 빌드 툴이라는 것을 사용했으면 하는 마음이었다. 물론 사내 보안 규칙으로 빌드툴은 사용이 안되었지만 이런 빌드툴로 인하여 우리가 이런 일련의 과정은 일일이 하지 않아도 되기 때문이다.빌드툴소스코드의 빌드 과정을 자동으로 처리해주는 프로그램외부 소스코드 자동 추가 관리빌드툴에는 Ant, Maven, Gradle이 있지만 유연함과 성능으로 Gradle이 압승으로 많은 사람들이 Gradle을 사용한다.Day1스프링 프로젝트를 시작하는 두 번째 방법스프링 프로젝트를 시작하는 두 번째 방법은 start.spring.io를 이용하는 방법이다. 즉, spring initializr를 이용하는 방법이다.이 방법 또한 나는 많이 사용해봤기 때문에 쉽게 수강을 할 수 있었다. 그래도 복습겸 열심히 들어봤다.처음에 빌드 툴을 설정하는게 나온다. 신규 프로젝트는 Gradle을 사용한다. 언어는 자바, 코틀린, 그루비를 선택하게 되어있는데 최신에는 코틀린을 많이 선택한다. 다음으로 스프링 부트 버전을 선택하는게 나오는데 여기서 알파벳이 붙은 버전은 오픈베타버전으로 가급적 알파벳을 붙이지 않는 것을 선택하는게 좋다. 다음으로 프로젝트 메타데이터를 작성하는게 나오는데 각각은 아래의 의미를 가진다.Group : 프로젝트 그룹Artifact : 최종 결과물의 이름Name : 프로젝트 이름Description : 프로젝트 설명Package name : 패키지 이름다음으로 패키징 방법을 선택하는게 나오는데 우리는 jar를 선택했다. 일종의 압축파일이다. 요즘 많이 사용하며, 특정 SI 프로젝트의 경우 War를 많이 사용하기도 한다. (내 경험담...)다음으로 자바 버전을 선택하는데 코치님은 자바17을 선택하셨지만 나는 21이 나온 시점이라 21을 선택하였다.다음으로 의존성 설정한다. 여기서 의존성이란, 프로젝트에서 사용하는 라이브러리 / 프레임워크를 의미한다. 📚 라이브러리란?프로그래밍을 개발할 때 미리 만들어져 있는 기능을 가져다 사용하는 것!코치님은 일종의 김치찌개로 비유하셨다. 김치찌개를 끓일 때 김치를 직접 농사해서 할 수 있고 마트에 살 수 있다. 여기서 마트의 김치를 라이브러리에 비유하셨다.나는 비유를 밀키트로 비유해보겠다. 떡볶이 밀키트가 있다하면 떡볶이를 직접 재료를 사서 조리를 할 수 있지만 밀키트를 사서 쉽게 끓여먹을 수 있다. 📚 프레임워크란?프로그래밍을 개발할 때 미리 만들어져 있는 구조에 코드를 가져다 끼워 넣는 것!이것도 김치찌개로 비유하셨는데, 여러 재료를 사서 만들 수도 있고 원데이 클래스에 가서 선생님이 시키는 것만 편하게 할 수도 있다. 여기서 원데이 클래스가 프레임워크라 하셨다. 마지막으로 의존성을 설정했으면 generate을 눌러서 압축파일을 다운 받고 아까 설명한 첫번째 방법을 이용하여 인텔리제이로 생성한 프로젝트를 켠다. @SpringBootApplication과 서버서버란? 내가 생각하는 서버는 영어로 serve는 "제공하다"라는 의미를 지닌다. 어떤 것을 제공하는 사람을 서버라고 부른다. 우리는 식당에 가면 종업원이 서빙을 한다. 즉 서버가 서빙을 하는 것이다. 즉, 기능을 제공하는 프로그램이여, 그 프로그램을 실행시키고 있는 컴퓨터를 서버라고 한다. 여기서 이런 의문사항이 있을 수 있다.🙋🏻 서버를 들었을 땐 엄청 크고 멋진 장치인데 그거랑 뭐가 다를까?서버라고 하면 엄청나게 큰 장치만 생각하며 막연하게 생각하신 분들이 많을 것이다. 컴퓨터의 외형으로 서버와 클라이언트를 나누는 것이 아니다. 서버는 단지 서비스를 제공해준다는 것만 기억하면 될 것이다. 우리가 사용하는 컴퓨터도 언제든지 서버가 될 수 있다.  나는 대학생 때 캡스톤 디자인으로 라즈베리파이라는 초소형 컴퓨터를 구입하여 서버로 이용하기도 했다. 손바닥만한 작은 크기지만 서버의 역할을 잘 수행하였다. 다만 대부분의 서버는 많은 클라이언트의 요청을 처리해야 하므로 성능이 중요하다. 따라서 하드웨어의 크기도 커진 것이다. 하지만 서버와 클라이언트에서 중요한 것은 하드웨어의 크기가 아니라 "누가 요청을 하고 누가 응답을 받는가"이다.여기서 클라이언트라는 말도 나온다. 클라이언트는 요청하는 사람 혹은 컴퓨터라고 한다. 그럼 이 클라이언트는 어떻게 서버에게 요청을 할까? 바로 인터넷을 통해 한다. 네트워크란 무엇인가?!네트워크를 이세계의 부족으로 설명해주셨다. 이세계 부족에는 주소체계와 택배시스템이 잘 되어 있다. 그래서 우리가 택배보내는 것처럼 아래와 같이 택배를 보낼 수 있다고 하셨다. B부족 감자동 곰로 13번길 2에 사는 둘째하지만 이렇게 주소체계를 우리도 마찬가지로 기억하는 사람은 많지 않을 것이다. 그냥 '파란색 집에 사는 둘째'라고 편히 부른다. 이제 현실세계도 마찬가지다. 현실세계에 컴퓨터는 고유의 IP를 가진다. 그리고 현실세계는 택배시스템처럼 인터넷이 잘 발달되어 있다. 그래서 우리는 인터넷을 통해 데이터를 주고 받을 수 있다. 아래와 같이 말이다.210.210.210.210 IP를 가진 PC에서 port 8080번으로 데이터 보내줘!파란색집 둘째가 port이고 자세한 주소가 IP 주소이다. 하지만 우리는 인터넷 접속할 때 일반적으로 IP주소와 port를 입력하지 않는다. 아래와 같이 도메인을 입력하고 접속할 것이다.https://www.spring.io:3000여기서 210.210.210.210이 spring.io일 것이다. HTTP와 API란 무엇인가?!HTTP와 API를 설명을 위해 또 다시 이 세계를 비유해주셨다. 택배를 보내려면 우리는 운송장이란 표준을 이용한다. 이세계의 운송장은 아래와 같다.내놓아라 파란집 둘째, 포션 빨강색 2개여기서 '내놓아라'는 운송장을 받는 사람에게 요청하는 행위이며, '파란 집'은 운송장이 가는 집을 말하고, '둘째'는 운송장을 실제 받는 사람, '포션'은 운송장을 받는 사람에게 원하는 자원이며, '빨강색 2개'는 자원의 세부조건을 의미한다. 여기서 행위와 자원은 빨간집에 운송장을 보내기 전에 약속해야 한다.현실세계에도 데이터를 받는 표준이 있는데 바로 HTTP이다. 일종의 약속이다. 아래와 같이 약속을 지켜 우리는 데이터를 보낸다.GET /portion?color=red&count=2Host: spring.io:3000여기서 GET은 HTTP 요청을 받는 컴퓨터에게 요청하는 행위이며, HTTP method라고 부른다. Host 부분은 HTTP 요청을 받는 컴퓨터와 프로그램 정보를 뜻한다. /portion은 HTTP 요청을 받는 컴퓨터에게 원하는 자원을 의미하며, path라고 부른다. ?은 구분기호이며 color=red는 자원의 세부조건, &는 구분기호, count=2 또한 자원의 세부조건을 뜻한다.행위와 자원은 HTTP 요청을 보내기 전에 약속해야 한다.그리고 이런 세부조건들을 고급용어로 쿼리스트링라고 부른다. 또한 이세계에서 아래와 같이 운송장을 작성할 경우도 있다.창고에 넣어라, 오크가죽, 창고에이것을 현실세계로 표현하면 다음과 같다.POST /oak/leatherHost: spring.io:3000오크가죽정보여기서 다른 것은 위와 동일하지만 '오크가죽정보'는 body라고 하고 호스트 부분과 한줄 내리고 시작을 한다. 요약을 하면 GET HTTP method는 데이터를 요청하는것으로 보통 쿼리스트링을 이용한다. (없는 경우도 있음) 하지만 POST는 데이터를 저장을 하는 것으로 바디를 이용한다. 이외에 PUT과 DELETE가 있는데 PUT은 데이터 수정을 요청하는 것으로 바디를 이용하고, DELETE는 데이터 삭제요청을 하는 것으로 쿼리스트링을 이용한다. 그럼 API는 무엇일까? API란, 정해진 약속을 하여, 특정 기능을 수행하는 것이다. 그래서 이 약속은 이전까지 썼던 방식으로 첫줄에는 HTTP method와 path, (쿼리)를 작성한다. 추가적으로 어디로 보낼 지 Host를 작성한다.(도메인 + 포트) 이런것을 헤더를 작성한다고 하고 헤더는 여러줄이 가능하다. 그 다음 body가 있을 경우 한 줄 띄고 body를 작성하며 여러줄 작성이 가능하다. 그래서 https://spring.io/portion?color=red&count=2 이런 형식을 URL이라고 부르고 작성 순서는 아래와 같다.프로토콜://도메인(혹은 IP:포트)/자원경로?쿼리(추가정보)그럼 요청을 보냈으니 응답을 보내줘야 한다. 예를 들어 200 OK 이런식으로 말이다. 요청에 대한 응답을 보내주는 컴퓨터를 서버라고 부른다. 그리고 요청을 한 컴퓨터를 클라이언트라고 부른다. 또한 응답에는 body를 담을 수도 있다. 응답은 요청 구조와 동일하다. 그리고 응답의 핵심은 상태코드인데 200, 201, 400, 404, 500등이 존재한다. GET API 개발하고 테스트하기API를 개발 전에는 항상 API Spec을 살펴봐야 한다. 즉, HTTP method와 path, 쿼리를 봐야하고 이에 대한 응답에 결과도 확인을 해봐야 한다. 그래서 실제 더하는 GET API를 실습을 해보았다.여기서 실습중에 @RestController라는 어노테이션도 학습을 했는데, 해당 클래스를 API의 진입점으로 만드는 어노테이션이라고 볼 수 있다. 그리고 @GetMapping("/path")이라는 어노테이션도 학습을 했는데 해당 메서드를 HTTP method가 GET이고 path가 /path인 API로 지정한다는 의미이다. 마지막으로 @RequestParam을 배웠는데 쿼리가 있을 시, 주어진 쿼리를 함수 파라미터로 넣을 수 있다. 그래서 단일 타입으로 넣을수도 있지만 request DTO를 만들어 객체를 넣을 수도 있는데 객체를 넣을 시, 어노테이션은 생략할 수 있다. 단, Spring Boot 3.2 이후 버전은 생략이 불가능했는데 빌드 툴을 Gradle로 변경하면 가능했었다. 왜 그런지는 내가 작성한 미션1에 대한 내용을 살펴보자. 📚 미션1https://inf.run/QKGsfDay2POST API 개발하고 테스트하기POST에는 어떻게 데이터를 전송하고 받을지에 대해 학습을 했다. POST에서는 GET과 달리 HTTP Body를 이용하였다. 그리고 HTTP body는 JSON 형태로 보낸다. 객체 표기법, 즉 무언가를 표현하기 위한 형식이다! Java로 비유해보자면, Map<Object, Object> 느낌이다.JSON의 표기 예는 아래와 같다.{ “name”: “양성빈”, “age”: 29, "stack": ["java", "javascript"], "house": { "address": "대한민국 경기도 시흥", "hasDoor": true } }그래서 POST HTTP method로 body를 넘겨 보낼 때 이런 형식으로 보낸다.그리고 실습을 통해 POST method를 실습했다. 여기서 나온 주요 어노테이션은 아래와 같다.@PostMapping("/path") : 아래 함수를 HTTP Method가 POST이고 Path가 /path인 API로 만든다!@RequestBody: HTTP Body로 들어오는 JSON을 파리마터로 넘긴 객체(DTO)로 바꿔준다. 그리고 DTO에는 json의 key값이 명시되어야 하며, 각 속성은 key값과 동일하게, 타입도 value에 타입에 따라서 작성한다.유저 생성 API 개발실제 프로젝트에 대한 기능 스펙을 제시해주셨으며 웹 UI까지 제공해주셨다. 그리고 우리가 배운 POST를 이용해 유저생성 API를 개발해보았다. 이 부분도 내가 아는거라 간단히 편하게 실습을 할 줄 알았지만, 내가 미쳐 생각지 못한 부분이 있었는데 이 부분을 다시한번 복습하는 계기로 실습을 하였다. 유저 조회 API 개발과 테스트이제 유저 조회 API를 실습해보았다. 이전에 배운 GET HTTP method를 이용하여 개발했다. GET API에서는 응답 반환이 있었는데 이 형태는 json이였으며 json으로 반환받으려면 파라미터로 넘기는 객체(DTO)에 getter가 반드시 있어야 json으로 받을 수 있다. 이 부분이 내가 배운 사실이었다.  📚 참고한 클래스 안에는 여러 API 추가 가능 정리. 다음으로!우리가 이렇게 GET과 POST API를 설계하고 개발하고 테스트까지 해보았다. 하지만 지금까지 만든 프로젝트에 큰 문제가 있다. 서버를 재시작하면 데이터가 날라갔다. 그 이유를 나는 잘 몰랐고 DB를 안 써서 그랬겠지라는 생각이었는데 코치님께서 아래와 같이 설명해주셨다.컴퓨터에는 1차 메모리와 2차 메모리가 있고 데이터가 날라가는 이유는 1차 메모리에 있었기 때문이다. 그래서 서버를 재시작해도 데이터가 남아있으려면 2차 메모리에 저장을 해두어야 하는데 우리는 2차 메모리에 저장보단, DB에 저장한다고 하셨다.Day3Database와 MySQL지난번에 우리는 서버를 재가동하면 데이터가 남아있지 않고 사라졌다. 그 이유는 유저 데이터가 램에 저장되어 있기 때문이다. 그래서 우리는 2차 메모리등에 저장하는 방법을 생각할 수 있다. 자바의 File이라는 클래스를 이용해 직접 디스크에 접근을 할 수 있지만 보통은 Database를 이용한다.Database란 데이터를 구조화시켜 저장하는 하는 것이라고 볼 수 있다. 마치 엑셀과 비슷하다고 생각하면 좋을 것 같다. 엑셀처럼 데이터를 표처럼 구조화하여 저장한다. 대표적으로 RDB의 MySQL이 그렇다. 그리고 이 표처럼 구조화된 데이터를 조회하는 언어를 SQL이라고 한다.MySQL에 접근하는 방법은 먼저 MySQL을 시작해야 한다. 인텔리제이 얼티밋 유료버전을 이용하면 IDE에서 직접 접근이 가능하지만 이 IDE를 이용할 수 없는 분들은 윈도우의 cmd창이나 유닉스의 터미널을 이용해야 한다. 동일하게 아래의 명령어를 작성하면 된다. $> mysql -uroot -p MySQL에서 테이블 만들기테이블 하나를 만든 다는 것은 엑셀파일을 만드는 것인데, 엑셀파일을 만들려면 엑셀파일을 담을 폴더를 생성 후에 폴더에 들어가 엑셀파일을 생성해야 한다. 그리고 엑셀에 헤더를 작성해야 한다. 그리고 헤더별로 서식을 설정한다. MySQL 테이블 생성도 이와 유사하다. 과정은 아래와 같다. 여기서 폴더는 데이터베이스(스키마)를 엑셀파일은 테이블을 엑셀파일의 헤더는 테이블의 필드를 정의한 것이다. 그리고 엑셀파일의 서식은 테이블의 필드의 타입을 설정하는 것이라 볼 수 있다. 그럼 이제 DB의 테이블을 직접 만들어보자.데이터베이스 만들기$> create database [데이터베이스 이름];데이터베이스 목록보기$> show databases;데이터베이스 지우기$> drop database [데이터베이스 이름];데이터베이스 접속하기$> use [데이터베이스 이름];테이블 목록보기$> show tables;테이블 만들기$> create table [테이블 이름] ( [필드1 이름] [타입] [부가조건], [필드2 이름] [타입] [부가조건], ... primary key ([필드이름]). );테이블 제거하기$> drop table [테이블 이름];💡 꿀팁1. auto_increment: 데이터를 명시적으로 넣지 않더라도 1부터 1씩 증가하며 자동 기록된다. 단, 데이터를 생성하고 삭제를 한 후 다시 생성시 1부터 생성되는게 아니라 삭제한 컬럼의 id값 다음 값으로 생성된다. 그리고 데이터 추가시에, auto_increment로 설정한 필드는 안 넣어도 자동으로 들어간다.2. primary key: 유일한 필드를 지정할 때 사용MySQL 타입정수타입: tinyint(1 byte), int(4byte), bigint(8byte)실수타입:double(8 byte)decimal(A, B): 소수점 B개를 가지고 있는 전체 A자릿 실수문자열 타입:char(A): A글자가 들어갈 수 있는 문자열varcher(A): 최대 A 글자가 들어갈 수 있는 문자열날짜, 시간 타입date : 날짜, yyyy-MM-ddtime : 시간, HH:mm:ssdatetime : 날짜와 시간을 합친 타입, yyyy-MM-dd HH:mm:ss지금까지 배운 SQL을 DDL(Data Definition Language)이라고 한다. 즉, 데이터 정의 언어라고 말한다. 테이블의 데이터를 조작하기데이터 넣기$> INSERT INTO [테이블 이름] (필드1이름, 필드2이름, ...) VALUES (값1, 값2, ...)데이터 조회하기$> SELECT * FROM [테이블 이름]; // * 대신에 필드 이름 여러개 넣을 수 있다.$> SELECT * FROM [테이블 이름] WHERE [조건]; // 특정 조건을 통해 조회. AND 또는 OR을 이용해 조건을 이어 붙일 수 있다! 조건에는 =, <= 외에도 !=, <, >, >=, between, in, not in 등이 있다.데이터 업데이트$> UPDATE [테이블 이름] SET 필드1이름=값, 필드2이름=값, ... WHERE [조건];⚠ 주의만약 [조건]을 붙이지 않으면, 모든 데이터가 업데이트된다!!!데이터 삭제하기$> DELETE FROM [테이블 이름] WHERE [조건];⚠ 주의만약 [조건]을 붙이지 않으면, 모든 데이터가 삭제된다!!지금까지 배운 SQL을 DML(Data Manipulation Language)이라고 한다. 즉, 데이터 조작 언어이다. Spring에서 Database 사용하기지금까지 사람이 DB에 직접 접근했으니 웹 어플리케이션이 DB에 접근하도록 하겠다.먼저 src/main/resources의 경로에 application.properties가 있을 것이다. 이것을 application.yml로 변경해준다. 단, 강의에 따라 변경을 한것이지 properties가 더 익숙하신 분이면 여기다가 DB설정정보를 기입해도 된다.아래와 같이 DB 설정정보를 기입한다.spring: datasource: url: "jdbc:mysql://localhost/library" username: "root" password: "1234" driver-class-name: com.mysql.cj.jdbc.Driverjdbc:mysql:// - jdbc를 이용해 mysql에 접근localhost – 접근하려는 mysql은 localhost에 있다./library – 접근하려는 DB는 library이다.root는 MySQL에 접근하려는 계정명1234는 MySQL에 접근하기 위한 비밀번호마지막으로 driver-class-name은 데이터베이스에 접근할 때 사용할 프로그램이 적혀있다.그럼 이전에 만든 프로젝트에 DB를 입히기 위해 유저 정보 테이블을 만든다.그후에, 유저 생성 API를 JdbcTemplate을 이용하여 SQL을 날린다. 생성자를 만들어 jdbcTemplate을 파라미터로 넣으면, 자동으로 들어온다. 그리고 SQL을 문자열로 입력 후, 값이 들어갈 부분에 ?을 넣는다. ?를 사용하면 값을 유동적으로 변경이 가능하다. 그리고 이 문자열을 JdbcTemplate의 update 메서드에 담는다. update 메서드는 insert, update, delete 쿼리에 적용이 가능하다.다음 유저 조회 API도 변경한다. 아래와 같이 변경이 가능하다.jdbcTemplate.query(sql, RowMapper 구현 익명클래스)구현 익명클래스 안에는 ResultSet에 getType(“필드이름”)을 사용해 실제 값을 가져올 수 있다. 그리고 이 익명클래스는 람다식을 이용하면 더 간단하게 표현이 가능하다.Day4유저 업데이트 API, 삭제 API 개발과 테스트이제 DB를 이용해 유저 업데이트와 삭제 API를 개발해보았다. 업데이트는 UPDATE 쿼리를 사용하여 jdbcTemplate의 update 메서드에 넘겨주어 실행을 하였고, 업데이트는 body를 넘기므로 @RequestBody를 사용하였다. 그리고 아래와 같은 추가 어노테이션도 학습하였다.@PutMapping("/path"): /path 경로로 PUT HTTP method를 전송한다.삭제 또한 마찬가지다. DELETE 쿼리를 이용하여 jdbcTemplate의 update 메서드에 넘겨주어 실행을 하였고 삭제는 파라미터를 넘기므로 @RequestParam을 사용하였다. 그리고 아래와 같은 추가 어노테이션도 학습을 하였다.@DeleteMapping("/path"): /path 경로로 DELETE HTTP method를 전송한다.유저 업데이트 API, 삭제 API 예외 처리 하기여기서 또 하나 꿀팁은 존재하지 않는 유저를 업데이트하고 삭제해도 200OK 응답이 나오는 것이 문제였다. 그래서 Exception을 던져서 500 INTERNAL SERVER ERROR가 나오게 하는것으로 변경을 하였다. 그래서 업데이트든 삭제든, select 쿼리를 전에 날려서 유저가 존재하는지 유무를 판단후, 있으면 각각 업데이트, 삭제 쿼리를 날리고 없다면 IllegalArgumentException을 날라기로 변경하였다. 아래와 같이 select 쿼리를 날리 수 있다.String readSql = "select * from user where id = ?"; return jdbcTemplate.query(readSql, (rs, rowNum) -> 0, id).isEmpty();이렇게 return된 boolean 변수를 통해 유무 판단을 하는 것이다. Section2 정리. 다음으로!지금 코드에도 문제가 존재한다. 바로 한 클래스인 Controller에 많은 역할을 하며 여러 비즈니스 로직이 통합되어 있다.이 문제는 만약 이 코드가 1000줄 이상만 되도 어느 기능을 수정할 때 상당한 워킹타임이 들 것이다. 이런 문제를 어떤 방법론으로 어떻게 변경할지 알아보자.Day5좋은 코드(Clean Code)는 왜 중요한가?!코드는 요구사항을 표현한 언어이다. 개발자는 요구사항을 구현하기 위해 코드를 읽고 작성한다. 여기서 핵심은 읽는다는 것이다. 예를 들어 몇천줄의 코드에 변수도 의미없는 이름을 짓고 로직도 한 곳에 모여있다면 유지보수하는 개발자는 읽기도 힘들 것이다. 또한 동시에 여러명이 수정이 힘들고, 어느 부분을 수정하더라도 다른 곳에 영향을 끼칠 수 있기에 지뢰코드가 된다. 당연히 단위테스트는 힘들 것이다. 또한 안 좋은 코드가 쌓이면 시간이 지날수록 생산성이 떨어진다. 즉, 유지보수 시간이 늦어지고 이것은 바로 돈과 관계가 되기에 클린코드는 정말 중요하다. 그래서 클린코드를 정의하면 아래와 같다.함수는 최대한 작게 만들고 한 가지 일만 수행하는 것이 좋다.클래스는 작아야 하며 하나의 책임만을 가져야 한다.우리의 컨트롤러 클래스도 API 진입점 역할, 유저의 유무를 판단하는 예외로직, SQL을 통한 DB통신으로 무려 3가지 역할을 한다. Controller를 3단 분리하기 – Service와 Repository우리의 컨트롤러는 API 진입점 역할, 유저의 유무를 판단하는 예외로직, SQL을 통한 DB통신으로 무려 3가지 역할을 한다. 이것을 분리해보는 시간을 가졌다. API 진입점 역할은 컨트롤러 레이어 역할로 예외로직은 서비스 레이어 역할로, SQL을 통한 DB통신은 레파지토리 레이어 역할로 분리하였다. 이렇게 레이어 분할을 한 구조를 Layered Architecture라고 한다.Live Q&A금요일에 Live Q&A를 참석했다. 커뮤니티에 올린 질문들을 코치님께서 성심 성의껏 말씀을 해주셔고 약간의 시간이 남아서 웹 어플리케이션 서버와 웹서버 차이를 역사를 통해 알려주셨다.초기에는 원격으로 메세지를 보내는 방식에서 시작했다가 이후에 클라이언트가 서버에게 정적 리소스를 요청하는 걸로 발전했다. 즉, 어떤 클라이언트가 요청을 하든지 똑같은 내용이 오는 것이다. 이것을 웹 서버라고 하고 대표적으로 Apache와 NginX가 있다. 이러다가 이런 생각도 하게 되었다. 클라이언트마다 다른 리소스를 받고 싶다는 생각을 하게 되었다. 그래서 클라이언트가 서버에 요청을 하면 서버는 요청을 확인해 그에 맞는 프로세스를 실행하여 파일을 그때 그때 바꾼다. 하지만 이런 과정은 성능적으로 좋지 않다. 그래서 이런식으로 변경을 했다. 클라이언트가 요청을 하면 서버는 요청을 받고 쓰레드를 생성한다. 쓰레드는 서블릿이라는 인터페이스 통해 알려준다. 즉, 여기서 서버가 생성한 프로세스는 Servlet Container라고 하고 쓰레드를 쓰레드 풀에 담아 관리한다.여기서 또 생각한 것은 서블릿은 여러 공통코드가 많아 우리가 개발을 할때 공통코드를 적느라 비효율적이라 느껐다. 그래서 서블릿을 그때 그때 사용하지 않고 하나로 퉁 치는 개념이 등장했는데 그것을 Dispatcher Servlet이 등장하게 되었다.미션 해결 과정Day1첫번째 미션은 어노테이션에 대한 학습을 하는 것이였다. 아래 질문을 통해서 말이다.어노테이션을 사용하는 이유 (효과) 는 무엇일까?나만의 어노테이션은 어떻게 만들 수 있을까?여기서 나는 이런 질문도 질문이지만, 단순히 @붙이는 걸로 파악하고 있었다. 그래서 이 어노테이션에 대한 기본 문법, 커스텀 어노테이션에 대해 알아보면서, 자바 표준 어노테이션은 무엇이 있고 각각 무슨 의미를 하는지 학습을 찾아봤으며, 찾다보니 자바의 리플렉션 개념까지 연관이 되었다. 그래서 리플렉션에 대한 학습까지 이어갔다. @Documented를 붙은 어노테이션과 아닌 어노테이션이 어떤 차이가 있는지 java doc을 직접 만들면서 확인을 해보았고 자바8에 어노테이션의 변화에 대해서도 학습을 마쳤다. 그리고 자주 사용하는 롬복 어노테이션들이 어떤식을 동작을 해보는지 궁금하여 찾아보고 어노테이션 프로세서를 이용하여 조작을 하는 걸 알게 되었다. 즉, 나의 학습방식은 아래와 같았다.어노테이션이 뭐야? 어떻게 사용해? 동작원리는 뭐야? 각각의 어노테이션이 붙은거랑 아닌거랑 어떻게 달라?📖 학습 방법 및 반성할 점위의 물음을 재차 물으며 학습했다. 하지만, 반성할 점도 있었다. 하나의 개념으로 여러 개념들을 파보는 것은 좋지만 뭔가 실습을 많이 해보면서 익히면 체득이 될텐데 그러지 못했다는 점을 반성하게 된다. ㅠㅠ📋 미션 블로그https://inf.run/QKGsfDay2두번째 미션은 GET과 POST API에 대한 실습을 문제로 내주셨다. 여기서 나는 이런 생각을 했다. 단순히 문제 푸는것에 의의를 두셔서 문제를 내주신게 아닐 것이다. 좀 더 깊이 파보았다.문제를 풀 때 일단 먼저 풀고, 비즈니스 로직들을 서비스 레이어로 분리하여 해보고, DTO를 클래스가 아닌 JDK17에 나온 record를 이용도 해보고, 이에 Spring Boot 3.2에 나타는 트러블 슈팅도 겪었다. 그리고 검증에 대한 로직을 spring boot starter validation을 이용해 예외를 처리하며, 테스트코드까지 작성함으로 조금 더 깊이있게 해보았다. 📖 학습 방법 및 반성할 점나는 미션을 제출하면서 완벽히 진행을 했다고 느꼈다. 그리고 다른 러너분들이 제출한 글을 보니 의외로 나와 비슷한 부분도 있지만 또 다른 방법으로 제출하신 러너분들을 볼 수 있었다. 이에 나는 아직 부족하다라는 생각을 하며 좀 더 열심히 해서 성장해야겠다는 생각을 하게 되었다. 📋 미션 블로그https://inf.run/fJXgxDay3세번째 미션은 익명 클래스 / 람다 / 함수형 프로그래밍 / @FunctionalInterface / 스트림 API / 메소드 레퍼런스 라는 키워드를 생각하여 람다식과 익명클래스를 공부하는 것이였다.[질문]자바의 람다식은 왜 등장했을까?람다식과 익명 클래스는 어떤 관계가 있을까? - 람다식의 문법은 어떻게 될까?이 또한 나는 익명클래스의 어떠한 불편함때문에 왜 람다식이 등장한지를 질문사항으로 공부를 먼저 시작했다. 다음 람다식이 무엇인지 정의를 내려보았다. 또한 람다식을 공부하니 함수형 프로그래밍과 함수형 인터페이스가 연관되었으며 이에 대해 또 문법을 공부하고 이번엔 실습도 해보았다. 그리고 익명클래스와 람다가 어떻게 다른지를 코드로만 보는게 아니라 바이트코드를 확인하여 살펴봤다. 또한 더 깊게 파다보니 INVOKEDYNAMIC 내부 동작을 확인하게 되었다. 정말 깊게 팔 수록 한도 끝도 없다고 느끼게 되었다. 📖 학습 방법 및 반성할 점람다에 대해 처음에는 왜 등장했을까? 부터 시작해서 익명클래스와 람다가 어떤 차이가 있을지 코드뿐만 아니라 바이트코드로 확인을 했으며 더 깊이 들어가 INVOKEDYNAMIC 내부 동작을 학습해보는 계기가 되었다. 이런 미션을 하면서 "나는 이제까지 아무것도 아니었구나"라는 생각을 하며 더욱 더 열심히 하게되는 계기였다.Day4네번째 미션은 DB를 연동하여 API를 생성하고 수정하고 조회하는 것을 해보았다.당연히, 일단 나는 문제를 컨트롤러 클래스에 비즈니스, 예외로직을 넣고 해결했다. 이후에 나는 리팩토링 작업을 거쳤다. 이런 로직들을 서비스, 레파지토리로 분리하고 나는 엔티티라는 것을 따로 만들어 request와 response는 dto로 처리하고 엔티티는 순수히 데이터를 받는 걸로만 처리하여 더욱 견고히 했다. 이렇게 작성한 이유는 아래와 같다.보안: DTO를 사용하면 민감한 정보를 숨기고 필요한 데이터만 클라이언트에 전달할 수 있습니다.추상화: DTO는 엔티티의 구조를 클라이언트에 그대로 노출하지 않고, API 응답을 통해 데이터의 표현 방식을 커스터마이징할 수 있게 해줍니다.유연성: 엔티티와 API 사이의 계약을 DTO를 통해 정의함으로써, 엔티티의 변경이 API 스펙에 직접적인 영향을 미치지 않도록 합니다.또한 당연히, 테스트코드로 검증까지 완료하였다. 📖 학습 방법 및 반성할 점문제3번에 SUM이라든지 GROUP BY를 알긴 알았지만 이런 집계함수에 대해 정확히 뭔지가 헷갈렸던 부분이 많았다. 그래서 나름 검색도 해보고 사용법도 익혀보았다. 이로 인해 내가 SQL 부분을 완전히 아는게 아니라는 생각을 가졌고 시간날때 틈틈이 SQL 공부도 해보면서 자격증 시험(SQLD)도 준비해보면 좋지 않을까라는 생각을 가지게 되었다.Day5다섯번째 미션은 하나의 코드를 클린코드 개념을 도입해 리팩토링 하는 것이였다.나는 그래서 클린코드에 대해 검색을 하면서 다른 유튜브 영상을 통해 학습을 했고 이를 바탕으로 총 4~5단계에 걸쳐서 리팩토링을 하였다. 1단계는 단순히 변수이름 변경 및 메서드로 분리였지만, OOP 개념을 도입하고 단일책임의 원칙을 적용하였으며 마지막에는 팩토리 디자인패턴과 테스트 코드로 마무리하였다. 📖 학습 방법 및 반성할 점정말 이번 미션이 나를 반성하게 하는 점이였다. 현업을 뛰는 나로서 현업(프론트엔드)에서 내가 얼마나 더러운 코드를 짰다는 생각이 많이 들었다. 그 동안 나는 쓰레기를 생산했다고 할 정도로... 그래서 나는 다음주 출근하자마자 시간이 된다면 바로 리팩토링 작업을 시작해야겠다고 느끼게 된 하루였다.회고이번주부터 정말 정신이 없었다. 직장다니면서 끝나자마자 회사 근처 카페에 가서 미션 수행하고, 정말 정신이 없었다. 심지어 어느 하루는 날밤을 세서 한 적도 있었다. 하지만 오히려 힘들고 불행했다기 보단 행복했다. 다른 분들은 이상하게 느낄 지 모르지만 뭔가 해결했다는 쾌감이 정말 감명 깊었고 지금의 마인드를 기억하면 다음주도 화이팅해서 성장해보겠다.

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

Depth

인프런 워밍업 클럽 스터디 4기 - CS 전공지식 > 1주차 발자국

 ◼만들면서 쉽게 배우는 컴퓨터 구조Section1) 컴퓨터 구조 개요컴퓨터는 트랜지스터라는 반도체 소자로 만들어지고, 여러 트랜지스터로 NAND 게이트를 만들며, 이로부터 컴퓨터가 구성된다. 프로그램의 동작방식 (2가지)컴파일러전체 코드를 미리 번역해 실행 속도가 빠름컴파일 시 문법 오류 사전 발견 가능인터프리터한 줄씩 번역하며 실행, 실행 중 문법 오류 가능실행 속도는 컴파일러보다 느림 Section2) 컴퓨터 구성 요소CPU컴퓨터의 두뇌 역할을 하는 장치로, 산술연산장치(ALU), 제어장치 등으로 구성된다. 메모리RAM, ROM, 캐시 메모리 등이 있다. 주변 장치입력 장치 (키보드, 마우스 등), 출력 장치 (모니터, 프린터 등) 등이 있다. 8비트, 32비트, 64비트 컴퓨터가 표현할수 있는 데이터 차이8비트: 2^8 = 256개32비트: 약 42억개 (RAM 4GB 한계)64비트: 약 18경개 (RAM 한계없음, 거의 무한대)  Section3) 불 대수불 대수의 등장클로드 섀넌이 0과 1 (false, true)만으로 모든 논리 연산과 계이 가능함을 발견 주요 불 연산NOT: 입력의 반대값 출력AND: 둘 다 1일 때만 1 출력OR: 둘 중 하나 이상 1이면 1 출력NAND: AND의 결과를 반전NOR: OR의 결과를 반전XOR: 입력이 서로 다를 때 1 출력 (입력개수 2개일때), 1의 개수가 홀수이면 1, 아니면 0 (입력개수가 3개이상일때) 불 연산 우선순위 : NOT -> AND -> OR 불 대수의 성질과 법칙항등원 법칙, 교환법칙, 분배법칙, 동일법칙, 이중부정법칙, 흡수법칙, 드모르간 법칙 등이 있다 불 연산을 바탕으로 진리표 작성 가능방정식 -> 진리표 -> 논리회로로 변환 Section4) 비트10진법, 2진법, 16진법10진법은 0~9까지 10개의 숫자 사용2진법은 0과 1 두 가지 숫자만 사용16진법은 0~9, A~F(10~15)를 사용 바이트 저장 순서 방식리틀 엔디안: LSB(Least Significant Byte, 가장 낮은 자리 바이트)를 낮은 주소에 저장빅 엔디안: MSB(Most Significant Byte, 가장 높은 자리 바이트)를 낮은 주소에 저장  오버플로우표현할 수 있는 비트 수를 초과하는 계산 결과 발생 시 결과가 제대로 저장되지 못하는 현상 음수 표현MSB(Most Significant Bit, 최상위 비트)가 0이면 양수, 1이면 음수로 해석 2의 보수법음수를 표현하는 방법음수 = 1의 보수(모든 비트를 반전) + 1  Section5) 컴퓨터의 기초가 되는 하드웨어 만들기 Logisim-evolution 같은 시뮬레이터를 사용해 논리 회로를 직접 설계·실험 가능,설치하려면 JDK 필요, GitHub에서 최신 버전 다운로드 가 기본 논리 게이트NAND 게이트, NOT 게이트, AND 게이트, OR 게이트, XOR 게이트  Mission1)https://inf.run/wLEBH  ◼그림으로 쉽개 배우는 자료구조와 알고리즘Section1) 개요선형 자료구조 : 배열, 연결 리스트, 스택, 큐비선형 자료구조: 트리, 그래프, 힙 P-NP 문제 개념 이해빅오 표기법 (시간 복잡도)결정 문제와 최적화 문제P 문제 (Polynomial time 문제)NP 문제 (Nondeterministic Polynomial time 문제)NP-hard 문제P vs NP 문제   Section2) 트리와 이진트리트리노드(Node)와 간선(Edge)으로 구성된 계층적 자료구조 트리의 구성요소노드, 간선, 루트 노드, 자식 노드, 형제 노드, 리프 노드, 레벨, 높이 이진트리각 노드가 최대 2개의 자식 노드를 갖는 트리포화이진트리와 완전이진트리도 존  Section3) 이진 탐색트리각 노드가 값의 기준점 역할을 하며, 찾고자 하는 값이 현재 노드의 값보다 크면 오른쪽 서브트리를, 작으면 왼쪽 서브트리를 탐색하는 구조  Section4) AVL 트리높이 균형을 유지하는 이진 탐색 트리로, 각 노드의 왼쪽과 오른쪽 서브트리 높이 차이(균형 인수)가 최대 1 이내여야 한다.이 균형 조건을 벗어나면 회전 연산을 통해 트리의 균형을 맞춰 탐색, 삽입, 삭제 수행  🗒회고컴퓨터 구조와 자료구조(알고리즘) 강의를 동시에 완벽히 이해하기엔 시간적으로 무리가 있을 것 같아 컴퓨터 구조 심화 학습에 좀 더 집중해볼 예정이다.강의에서 Logisim-evolution을 활용해 AND, OR, NOT, XOR 등의 게이트를 직접 만들어보는 과정이 인상 깊었다. 회로를 하나하나를 쌓아가며 구조를 이해하는게 재미가 있어 몰입이 잘 되었다.강의를 완강할 즈음에는, 정말 작은 컴퓨터 하나쯤은 스스로 만들어 낼 수 있을 것 같은 기대감이 든다 🏷출처https://inf.run/y1hhdhttps://inf.run/7mNZ2https://inf.run/uHJ1a  

cs발자국4기

이승환

[워밍업 클럽 4기 백엔드] 1주 차 발자국 👣

학습 내용 요약 추상추상과 구체의 차이를 인식하고, 읽기 좋은 코드를 만들기 위한 추상화 기법을 배웠습니다. 논리, 사고의 흐름변수명 리팩토링, 매직 넘버/매직 스트링 상수 추출 등추상화된 데이터를 활용해, 최소한의 사고로도 이해할 수 있는 코드를 만드는 방법을 익혔습니다. 객체 지향 패러다임추상과 객체 지향의 관계를 고민해볼 수 있었고,SOLID 원칙을 코드에 어떻게 적용할 수 있는지 구체적인 방법을 배웠습니다. 객체 지향 적용하기가독성을 높이기 위해 객체를 더 잘게 나누고, 의미 단위로 분리하는 기법들을 배웠습니다.아직 익숙하지 않아서 많은 연습이 필요하겠지만, 굉장히 강력한 도구라는 점을 느꼈습니다.  학습 내용 회고 성취클린 코드가 왜 중요한지 근거를 들어 설명할 수 있게 되었습니다.추상화 기법을 데이터와 코드 관점으로 나누어 설명할 수 있게 되었습니다.SOLID 원칙을 이해하고, 설명할 수 있게 되었습니다.코드에 객체 지향과 추상화를 적용하고 있는지 스스로 의심하고 점검하는 눈이 조금씩 생기고 있습니다. 발견한 약점 및 보완이 필요한 부분좋은 추상화인지 판단하는 기준이 부족해, 메서드 이름을 지을 때 망설이게 되는 경우가 있었습니다.Java 기본 문법 중 일부가 기억이 흐릿하거나 헷갈리는 부분이 있었습니다.final 키워드를 사용하는 이유와 적용 범위stream API 중 flatMap() 처럼 자주 사용하지 않던 메서드의 반환 결과를 헷갈림강의에서 소개하는 리팩토링의 근거를 내가 제대로 이해하고 적용하고 있는지 점검할 기준이 필요하다고 느꼈습니다. 보완 계획좋은 추상화에 대한 판단 기준을 오픈 소스, 실무 코드, ChatGPT, 현직자 의견 등을 통해 배우고 정리할 예정입니다.헷갈리는 Java 문법은 노션에 기록하며 정리하고, Stream API는 워밍업 클럽 이후 강의를 통해 추가 학습할 계획입니다.강의에서 알려주는 리팩토링 근거를 제대로 인지하며 따라하고 있는지, 평가 기준과 프로세스를 만들어 적용 중입니다.강의가 끝난 후 기록해둔 리팩토링 근거를 보며 리팩토링 연습같은 내용을 근거를 보지 않고 리팩토링 시도 (근거가 떠오르지 않을 경우 체크해두기)리팩토링 흐름을 떠올리며 기록해보기근거를 모두 떠올리고 적용할 수 있다면 통과  미션 내용 회고 Day2 미션 🌱 첫 번째 미션 🏃추상과 구체의 예시를 표현해보기 미션 과정BBQ 자메이카 통다리 구이를 배달시켜 먹는 과정을 추상으로 정의하고, 구체로 표현해봤습니다. 느낀점일상 속 많은 개념이 이미 추상화 되어있다는 것을 새삼 느꼈습니다.정보통신 기술로 문제를 해결하거나 사업을 하기 위해선,이런 일상 속 추상화를 인식하고, 구체로 바꿔보는 훈련이 반드시 필요하다는 것을 체감했습니다. Day4 미션🌱 첫 번째 미션 🏃예시 코드를 [섹션 3. 논리, 사고의 흐름 내용]을 중심으로 읽기 좋은 코드로 리팩토링하기 미션 과정“뇌 메모리에 최소한의 정보만 있더라도 코드를 이해할 수 있는가?”를 기준으로 리팩토링을 진행했습니다.강의에서 언급된 기법들을 코드에 실제로 적용해보며 다음과 같은 고민을 하게 되었습니다.올바른 근거를 가지고 추상화를 하고 있는가?이 메서드명이 실무에서 사용하는 방식일까? 아니라면 실제로는 어떤 방식을 사용할까?Spring 프레임워크를 사용할 때와 순수 Java 프로젝트에서 추상화 방식에 차이가 있을까? 차이가 있다면 어떤 차이일까?검색, ChatGPT, GitHub 소스 코드, 그리고 사람들의 의견을 통해 이 고민들을 풀어나가며 많은 배움을 얻었습니다. [워밍업 클럽 4기 백엔드] Day4 미션Day4 미션 해결 과정 느낀점리팩토링 과정에서 고민 끝에 스스로 찾아가며 얻은 지식이라 더 오래 기억에 남을 것 같습니다.워밍업 클럽이 끝난 이후에도 스터디를 통해 더 많은 예제를 읽기 좋은 코드로 리팩토링해볼 계획입니다! 🫡  두 번째 미션 🏃SOLID를 자기만의 언어로 정리하기 미션 과정SOLID 원칙을 영단어의 어원과 이미지로 해석해보고 저만의 언어로 정리해보았습니다. 느낀점평소 학습할 때 원리와 배경을 이해하는 것을 중요하게 생각하는 편인데, 이번 시도는 특히 이해에 큰 도움이 되었습니다.한글로는 뜬금없이 느껴지던 개념들도, 영단어의 어원과 이미지를 통해 숨겨진 의미를 이해할 수 있다는 점이 정말 흥미로웠습니다.앞으로도 개념을 학습할 때, 단어나 용어에 담긴 이미지와 어원, 뉘앙스를 통해 더 깊이 이해하려는 시도를 꾸준히 이어나갈 계획입니다. 강의Readable Code: 읽기 좋은 코드를 작성하는 사고법

백엔드워밍업클럽백엔드발자국

이승환

[워밍업 클럽 4기 백엔드] 4주 차 발자국 👣

학습 내용 요약 Spring & JPA 기반 테스트 (Presentation Layer 테스트 (2) ~)@WebMvcTest를 활용한 컨트롤러의 테스트 방법을 학습했습니다. Mock을 마주하는 자세테스트 하기 어려운 영역을 어떻게 분리하고 처리할 수 있는 지에 대한 방법으로 Mock에 대한 개념을 학습했습니다.Mockito를 활용하여 Test Fixture를 준비하는 과정도 함께 배웠습니다. 더 나은 테스트를 작성하기 위한 구체적 조언가독성 높은 테스트 코드를 작성하는 방법과,스프링 서버 구동 횟수를 줄이는 방법 등을 배울 수 있었습니다.@DynamicTest, @ParameterizedTest 같은 다양한 테스트 방식도 익힐 수 있었습니다. Appendix학습 테스트와 Spring REST Docs에 대해 배웠습니다.설정이 복잡했지만 협업 시 매우 유용할 것이라는 느낌을 받을 수 있었습니다. Outro지금까지 배웠던 내용을 돌이켜보며,테스트가 필요한 이유에 대해 다시 한 번 생각해볼 수 있었습니다.  학습 내용 회고 🌱성취@WebMvcTest를 활용해 컨트롤러 테스트를 작성할 수 있는 역량을 갖추게 되었습니다.Mocking을 통해 테스트하기 어려운 영역을 분리하고 제어하는 방법을 익혔습니다.테스트 실행 환경을 통일해 스프링 서버 실행 시간을 단축할 수 있게 됐습니다. Spring REST Docs로 API 명세서를 작성하는 방법을 익혔습니다. ⚠발견한 약점 및 보완이 필요한 부분Day11 미션에서 전체 테스트를 실행하지 않고 미션을 제출하는 바람에, 깨지는 테스트를 포함한 채 제출했습니다. CSV를 읽어 객체로 변환하는 구현체에 집중하느라, 테스트 포인트 선정에 실패했습니다.CSV 파일은 항상 정해진 숫자만 읽어올텐데, 이런 경우에도 경계값 테스트를 해야할까?CSV 경로에는 null이 들어올 수 없을 것 같은데, null 테스트를 해야 할까?테스트 코드에 구현 레벨의 상세한 계산식을 그대로 드러냈습니다.setUp 메서드를 단순히 중복 제거 용도로만 인식했습니다. 📄보완 계획테스트를 작성 후 제출 전, 반드시 전체 테스트를 실행하겠습니다."이걸 꼭 검증해야할까?" 에 집중하기보단, 미래의 변경 가능성을 고려한 견고한 테스트를 작성하는 것에 집중하겠습니다.프로덕션 코드의 구현을 블랙박스로 보고, 입출력 검증을 중심으로 테스트하겠습니다.setUp은 단순히 중복 제거보다, 도메인에 집중하게 돕는 역할임을 인지하며 테스트 코드를 작성하겠습니다.   미션 내용 회고 Day16 미션 🌱 🏃첫 번째 미션Layered Architecture 구조의 레이어별 테스트 작성법을 알아보았습니다.레이어별로 1) 어떤 특징이 있고, 2) 어떻게 테스트를 하면 좋을지, 자기만의 언어로 다시 한번 정리해 볼까요? 미션 과정각 레이어의 관심사와 경계를 의식하며, 테스트 방법을 최대한 간단히 정리해보려 노력했습니다. 🔗미션 해결 링크https://inf.run/SoKcd  Day18 미션 🌱 🏃첫 번째 미션@Mock, @MockBean, @Spy, @SpyBean, @InjectMocks 의 차이를 한번 정리해 봅시다. 미션 과정강의에서 배운 내용을 기반으로 Mock과 Spy를 복습하며 차이점을 정리했습니다.MockBean과 SpyBean은 검색과 LLM을 활용해 추가 학습 후 요약했습니다. 🏃두 번째 미션아래 3개의 테스트가 있습니다. 내용을 살펴보고, 각 항목을 @BeforeEach, given절, when절에 배치한다면 어떻게 배치하고 싶으신가요?(@BeforeEach에 올라간 내용은 공통 항목으로 합칠 수 있습니다. ex. 1-1과 2-1을 하나로 합쳐서 @BeforeEach에 배치) 미션 과정가독성과 중복 제거의 관점으로 미션에 접근했습니다.온라인 세션 피드백을 통해,setUp은 단순 중복 제거가 아닌, 도메인에 집중할 수 있게 만들어 준다는 점을 깨달았습니다. 🔗미션 해결 링크https://inf.run/9LaUQ  느낀점 이번 워밍업 클럽은 저에게 두 번째 도전이었습니다. 작년에 이 강의를 처음 접했었는데,그 당시에 저는 Spring과 JPA를 잘 모르는 상태로 수강을 시작했었습니다."테스트 코드를 작성해보는 게 좋다"는 조언만 듣고 무작정 수강했지만,기초가 튼튼하지 않다보니 내용을 따라가는 것에 급급했고,이해가 부족해 강의 하나를 수강하는 데에도 시간이 세 배는 더 걸렸습니다.결국 완주도 하지 못한 채 아쉬운 마음으로 마무리하게 되었습니다. 그 때 다짐했습니다."이번엔 Spring과 JPA 기본기를 탄탄히 다진 후, 다시 도전해보자." 이후 약 4개월간,김영한 님의 Spring과 JPA 커리큘럼을 꾸준히 학습하며 기초를 탄탄히 쌓는 데 집중했습니다.매일 하루 대부분의 시간을 투자해 결국 전체 커리큘럼을 완주할 수 있었고,마침 워밍업 클럽이 다시 열린다는 소식을 접하게 되었습니다. 이번에는 제대로 이해해보자는 마음으로 워밍업 클럽에 다시 참여했고,스터디에도 적극적으로 임했습니다.덕분에 이전에는 막막하게만 느껴지던 개념들도이제는 한층 명확하게 다가왔고, 강의에서 강조하는 '가독성'에 집중할 수 있게 되었습니다. 이 경험을 통해,"이해는 반복과 지식의 축적 속에서 깊어진다"는 사실을 실감할 수 있었습니다.  [깨달음 1: 이해와 가독성의 연결 고리]수학 강사 시절부터"제대로 이해했다면, 초등학생도 이해할 수 있을 만큼 쉽게 설명할 수 있어야 한다"는 가치를 중요시 여겨왔습니다. 이번 워밍업 클럽에서 배운좋은코드는 쉽게 읽을 수 있어야 한다는 원칙은제 가치관과 맞닿아 있었고,이해와 가독성에 연결 고리가 형성되며가독성의 중요성에 대해 더욱 깊이 깨달을 수 있었습니다.  [깨달음 2: 코드 작성은 글쓰기와 비슷하다]또한 이번 학습을 통해"코드 작성은 글쓰기와 비슷하다"는 말을 처음으로 실감했습니다.그동안은 그저 인용구 같은 느낌으로만 흘려들었지만,이제는 코드와 글쓰기가 가독성의 관점에서 얼마나 닮아있는지 직접 경험하고 이해할 수 있게 되었습니다.  [앞으로의 계획]그래서 저는 앞으로 코드 작성과 더불어 글쓰기 능력도 함께 향상 시키고자 합니다. 우빈님께서 조언해주신 대로,좋은 문장을 필사하고, 구조를 분석하며, 블로그나 Notion에 꾸준히 글을 쓰며표현력과 구조화 능력을 함께 키워나갈 계획입니다. 그리고 코드 작성을 위해 학습할 서적은 다음과 같습니다.《클린 코드》《테스트 주도 개발》《파이브 라인스 오브 코드》그리고 추가로 우빈님께 추천 받은 《클린 아키텍처》 하나씩 차근차근 학습해나가겠습니다!  [마지막으로]온라인 세션을 통해 받은 두 번의 코드 리뷰 경험은 정말 값지고 기억에 남는 경험이었습니다. 항상 좋은 코드 리뷰를 갈망해왔기에이번 피드백은 특히 기억에 남았고,피드백을 통해 성장하는 즐거움도 함께 느낄 수 있었습니다. 반면,"앞으로도 이런 좋은 리뷰를 계속해서 받을 수 있다면 얼마나 좋을까.."하는 갈증도 함께 커졌습니다. 😅 사실 작년 인프런에서 진행된 네트워크 파티에 참여해 끝나갈 무렵, 우빈 님을 잠깐 뵌 적이 있습니다. 이야기를 나눌 수 있었던 시간은 아주 짧았지만,그 순간조차 저에게는 "더 열심히 해야겠다"는 동기 부여가 될 만큼 인상 깊었습니다.그 때 받았던 사인은 저에게 단순한 기념 이상의 의미였고,그 날 이후로 저도 우빈 님처럼 진지하게 학습하고 성장하는 개발자가 되고 싶다는 마음이 커졌습니다. 만약 우수러너로 선정되어 1:1 멘토링 기회를 얻게 된다면,그동안의 배움을 돌아보고 앞으로의 방향을 점검해볼 수 있는 정말 소중한 시간이 될 것 같습니다. 우빈 님께 많은 것을 배운 것처럼,저 또한 제가 받은 배움과 경험을 다른 이들과 나눌 수 있는 개발자,그리고 함께 성장하며 시너지를 만들 수 있는 백엔드 개발자로 나아가고 싶습니다.우빈님께 정말 많은 것을 배웠습니다.진심으로 감사드립니다. ️🙂‍↕️ 강의🔗Practical Testing: 실용적인 테스트 가이드

백엔드워밍업클럽백엔드발자국

이승환

[워밍업 클럽 4기 백엔드] 3주 차 발자국 👣

학습 내용 요약 Spring & JPA 기반 테스트 (Presentation Layer 테스트 (1) 까지)레이어드 아키텍처에서 각 레이어가 분리된 이유를 테스트 관점에서 이해할 수 있었습니다.cafeKiosk 프로젝트에 요구사항을 하나씩 추가해가며,Persistence → Business → Presentation Layer순서로 TDD 방식을 적용해 기능을 구현해보는 실습을 진행했습니다.  학습 내용 회고 🌱성취미션을 통해 총 15개의 단위 테스트를 작성하며, 테스트 작성에 익숙해질 수 있었습니다.boolean 변수명을 지을 때 자동사/타동사 구분을 고려하며, 더 명확한 이름을 짓게 되었습니다.레이어드 아키텍처의 레이어 분리 이유를 인식하고, 이를 기반으로 테스트를 구성할 수 있게 됐습니다.기능 전체를 TDD 방식으로 구현해보며 테스트 주도 개발에 대한 감을 익혔습니다.트랜잭션 전파 개념을 떠올리며 수동 클렌징과 자동 클렌징의 차이를 이해하게 되었습니다.@DataJpaTest와 @SpringBootTest의 차이점을 학습했고, profile을 통한 테스트 환경 분리에 대해서도 이해하게 되었습니다.   ⚠발견한 약점 및 보완이 필요한 부분TDD로 기능을 구현하는 과정에서, 중간 단계 테스트 작성을 종종 놓친 실수가 있었습니다.트랜잭션 전파, Spring AOP 개념 중 기억이 흐릿한 부분을 발견했습니다.@DisplayName의 명확하게 표현하는 데 어려움을 느꼈습니다.Day11 미션에서 Minesweeper 프로젝트의 단위 테스트 작성을 추가로 진행하지 못했습니다.  📄보완 계획TDD에 더욱 익숙해지기 위해 예제 프로젝트를 통한 실습을 추가로 진행할 예정입니다. Spring과 JPA의 작동 원리를 복습하며, 다음 항목들을 중점적으로 학습할 계획입니다.트랜잭션 전파Spring AOP JPA의 영속성 컨텍스트아직 작성하지 못한 Minesweeper 프로젝트의 단위 테스트를 작성합니다.예제를 통해 @DisplayName 작성에 더 익숙해지도록 합니다.  미션 내용 회고 Day11 미션 🌱 🏃첫 번째 미션[Readable Code] 강의의 두 프로젝트(지뢰찾기, 스터디카페) 중 하나를 골라, 단위 테스트를 작성해 봅시다.조건은 아래와 같습니다.✔각 프로젝트 모두 강의 중에 작성한 tobe 패키지 코드를 기준으로 함 (lesson 6-4 가 가장 마지막 버전)✔3개 이상의 서로 다른 클래스 & 총 7개 이상의 테스트 작성 (시간이 된다면 더 많이 작성해보면 좋겠죠? 😉)➡ 단, 같은 인터페이스를 구현하고 있는 구현체들은 1개 클래스로 간주한다.(ex. LandMineCell, NumberCell, EmptyCell에 각자 테스트를 작성했어도, 1개 클래스로 간주.)✔무엇을 테스트하고자 했는지를 잘 나타낸 @DisplayName 작성하기✔BDD(given/when/then) 스타일 따르기 (주석으로 표기) 🔗작업 브랜치 링크https://github.com/sonic8-8/readable-code/tree/day11-mission 미션 과정Day7 미션과 동일하게 사고 과정을 모두 Notion에 기록했습니다.다만 이번 미션에서는 문서 구조 자체의 가독성에 더욱 신경을 썼습니다.구체적으로는 다음 원칙을 적용했습니다.사고 과정은 하나의 흐름으로 기록하기 위해 Notion 문서에 작성한다.테스트 코드에는 주석을 통해 질문과 근거를 남긴다.  단위 테스트 작성 사고 과정🔗https://able-brick-82b.notion.site/Day-11-studycafe-20d2947a96118098986ad7e54c362460?source=copy_link 느낀점테스트 코드 역시 결국 읽기 좋은 코드여야 한다는 관점으로 미션을 수행했습니다.코드뿐만 아니라 문서 작성에서도 가독성을 어떻게 높일 수 있을지에 집중하다 보니,Notion이나 블로그 같은 일반적인 글쓰기에서도가독성을 높일 수 있는 방법에 대해 자연스럽게 고민하게 되었습니다. 주말 동안엔 이런 가독성 있는 코드 작성 방법에 대한 힌트를 얻고자 서점에 들렀고,클린 코드, 테스트 주도 개발, 파이브 라인스 오브 코드 등 책을 조금씩 읽어보며여러가지 인사이트를 얻을 수 있었습니다.특히 추상과 구체를 어떻게 분리해내는지에 초점을 두고 책을 읽으려했는데이 과정에서 코드의 구조를 바라보는 시야가 조금씩 확장되고 있다는 느낌을 받을 수 있었습니다. 다만, 책 분량이 많다 보니 단기간에 읽어내긴 쉽지 않다는 아쉬움도 있었습니다.그래서 워밍업 클럽이 끝난 이후에는 우빈님께 추천받은 위 서적들을 차근차근 학습하며,추상과 구체에 대해 더 고민하고 코드 작성 기본기를 탄탄히 다져나갈 계획입니다. 그리고 학습한 내용은 블로그에 저만의 방식으로 정리하고 포스팅 해서다른 사람에게도 도움이 될 수 있는 콘텐츠로 발전시켜 보고자 합니다. 🫡 강의🔗Practical Testing: 실용적인 테스트 가이드

백엔드워밍업클럽백엔드발자국

suover

인프런 워밍업 클럽 스터디 4기 - CS 전공지식 3주차 발자국

그림으로 쉽게 배우는 자료구조와 알고리즘 (심화편)만들면서 쉽게 배우는 컴퓨터 구조강의와 함께한 인프런 워밍업 클럽 스터디 4기 - CS 전공지식 (자료구조, 알고리즘, 컴퓨터구조)3주차 발자국 입니다.학습 내용 요약자료구조·알고리즘트라이(Trie)와 자동완성개념: 문자열 집합을 효율적으로 저장·탐색하기 위해, 각 노드가 문자 하나를 나타내며 공통 접두사를 공유하는 트리 구조입니다.학습: 삽입 및 탐색 로직의 흐름을 개념적으로 이해하였습니다.인사이트: 삽입·탐색이 문자열 길이에 비례하기 때문에, 대규모 단어 집합에서 접두사 검색이 빠르지만 메모리 사용이 커질 수 있음을 감안해야 함을 체감했습니다.그래프개념: 그래프는 정점과 간선으로 관계를 표현하는 자료구조로, 방향 여부나 가중치 유무에 따라 다르게 다루며 인접 리스트와 인접 행렬 방식으로 표현할 수 있습니다.학습: 간단한 예제 그래프를 구성해 보고, DFS는 재귀나 스택으로 깊이 우선 탐색하며 방문 체크로 순환을 막고, BFS는 큐로 레벨별 탐색해 비가중치 최단 경로에 유리함을 코드로 확인했습니다.인사이트: DFS는 깊이 탐색이 필요할 때, BFS는 단계별 탐색이 필요할 때 유용하며, 그래프 표현 방식도 데이터 크기와 목적에 맞춰 결정해야 한다는 점을 깨달았습니다.가중 그래프와 다익스트라 알고리즘개념: 가중치가 있는 그래프에서 시작 정점으로부터 다른 정점까지 최단 경로를 찾는 문제입니다.다익스트라 알고리즘원리: 현재까지 알려진 최단 거리 후보 정점을 선택해 인접 간선을 완화(relaxation)하며 거리 배열을 갱신합니다.인사이트: 그래프가 커질수록 일반 방식은 느려지고, 우선순위 큐(힙)를 쓰면 더 빠르게 최단 경로를 구할 수 있음을 느꼈습니다.컴퓨터 구조제어 장치 없는 컴퓨터 & 조립CPU 주요 구성 요소(CPU 레지스터, ALU, 프로그램 카운터, 스텝 카운터)를 복습하고, 실습을 통해 신호 흐름을 이해했습니다.CPU 만들기: 제어 장치(Control Unit)명령어 처리 흐름: 명령어 인출(fetch), 디코딩, 실행(execute) 단계의 제어 신호 타이밍을 확인했습니다.주요 명령어: NOP, LOADA, ADD, SUB, STOREA, LOADI, JMP/JMPC/JMPZ, OUT, HLT 등의 제어 신호와 ALU·레지스터·메모리 동작 방식을 이해했습니다. 제어 장치 조립: Logisim에서 각 단계별 신호를 조합해 제어 유닛을 완성하고, 시뮬레이션으로 신호 흐름과 분기 동작을 확인했습니다.미션🎯 자료구조·알고리즘 미션3: 허프만 코딩 구현목표주어진 문자열에 대해 HuffmanCoding.compress(str)를 호출했을 때, [ [문자, 코드], … ]형태의 배열을 출력하도록 허프만 코딩을 구현합니다.class Node { constructor(char = null, freq = 0, left = null, right = null) { this.char = char; this.freq = freq; this.left = left; this.right = right; } } class HuffmanCoding { compress(str) { if (!str) return []; // 빈도 계산 const freqMap = new Map(); for (const ch of str) { freqMap.set(ch, (freqMap.get(ch) || 0) + 1); } // 초기 노드 리스트 생성 const nodes = []; for (const [ch, f] of freqMap.entries()) { nodes.push(new Node(ch, f)); } // 허프만 트리 구성 while (nodes.length > 1) { nodes.sort((a, b) => a.freq - b.freq); const a = nodes.shift(); const b = nodes.shift(); nodes.push(new Node(null, a.freq + b.freq, a, b)); } const root = nodes[0]; // 코드 생성 const codes = {}; const build = (node, prefix) => { if (node.char !== null) { codes[node.char] = prefix || "0"; } else { build(node.left, prefix + "0"); build(node.right, prefix + "1"); } }; build(root, ""); // [문자, 코드] 쌍 배열 반환 return Object.entries(codes); } } const huffmanCoding = new HuffmanCoding(); const str = "Lorem ipsum dolor sit amet consectetur adipiscing elit. " + "Consectetur adipiscing elit quisque faucibus ex sapien vitae. " + "Ex sapien vitae pellentesque sem placerat in id. " + "Placerat in id cursus mi pretium tellus duis. " + "Pretium tellus duis convallis tempus leo eu aenean."; const result = huffmanCoding.compress(str); console.log(result);흐름 및 구현 요약빈도 계산문자열을 순회해 Map 또는 객체에 문자별 등장 빈도를 집계했습니다.코드 생성재귀 탐색으로 왼쪽 자식은 '0', 오른쪽 자식은 '1'을 prefix에 추가하며 리프에서 코드 할당. 단일 문자 입력 시 빈 prefix에 "0" 처리 로직도 반영했습니다.검증실행 결과에서 빈도가 높은 문자는 짧은 코드, 낮은 문자는 긴 코드가 할당되는지 확인했습니다.빈도와 코드 길이 관계가 적절한지, 모든 문자가 포함되었는지, 코드끼리 충돌이 없는지 점검했습니다. // 결과 [ [ 'i', '000' ], [ 'q', '001000' ], [ 'v', '001001' ], [ 'o', '00101' ], [ 'l', '0011' ], [ 'd', '01000' ], [ 'E', '0100100' ], [ 'g', '0100101' ], [ 'x', '0100110' ], [ 'P', '0100111' ], [ 'a', '0101' ], [ 'e', '011' ], [ 'm', '10000' ], [ 'r', '10001' ], [ 'u', '1001' ], [ 't', '1010' ], [ 'p', '10110' ], [ 'L', '10111000' ], [ 'C', '10111001' ], [ 'f', '10111010' ], [ 'b', '10111011' ], [ '.', '101111' ], [ ' ', '110' ], [ 's', '1110' ], [ 'c', '11110' ], [ 'n', '11111' ] ]느낀 점허프만 코딩의 "빈도 기반 병합 → 트리 구조 → 코드 생성" 흐름을 직접 구현하며 원리 습득이 명확해졌습니다. 결과 검증 과정에서 빈도와 코드 길이 관계, 프리픽스 성질 확인 등이 중요함을 체득했습니다. 🔗 자료구조·알고리즘 미션3 블로그 링크🎯 컴퓨터 구조 미션3: STOREB 명령어와 A·B 비교 어셈블리어 구현목표STOREB 명령어(OPcode 1001)를 추가해 레지스터 B의 값을 메모리 주소에 저장하도록 구현합니다.A와 B 값을 비교해 같으면 0, A>B면 1, A<B면 2를 출력 레지스터에 내보내는 어셈블리어를 작성하고 검증합니다.구현STOREB(1001) 명령어 추가기존 STOREA 처리 경로를 참고하여 opcode 1001 분기 로직을 제어 장치에 추가했습니다.스텝 카운터가 각 단계에 진입할 때, STOREB 신호와 연관된 핀들이 의도한 타이밍에 활성화되는지 확인했습니다.A·B 비교 어셈블리어메모리에 저장된 A와 B 값을 불러와 비교하는 순서LOADA addrA → SUB addrB (A = A - B, 플래그 설정)JMPC 분기로 A≥B 처리, 아닐 경우 LOADI 2로 A<B 결과 세팅 후 출력분기한 경우 ZF 검사: ZF=1일 때 A=B(LOADI 0), ZF=0일 때 A>B(LOADI 1)이후 OUT, HLT로 마무리주소 라벨 매핑과 분기 주소 계산을 꼼꼼히 검토했습니다.Logisim에서 다양한 A·B 조합 테스트를 통해 출력 레지스터가 기대값(0/1/2)을 잘 내보내는지 확인했습니다.LOADA 14 // A = RAM[14] SUB 15 // A -= RAM[15] JMPC 5 // CF = 1 → A ≥ B일 때 5번 주소로 점프 LOADI 2 // A < B → 결과 2 JMP 9 // 결과 출력으로 점프 JMPZ 8 // ZF = 1 → A = B일 때 8번 주소로 점프 LOADI 1 // A > B → 결과 1 JMP 9 // 결과 출력으로 점프 LOADI 0 // A = B → 결과 0 OUT // 결과 출력 HLT // 프로그램 종료 0 0 0 7 // A값 3 // B값느낀 점제어 장치에 새로운 명령어를 추가할 때, 기존 신호 경로와 타이밍 흐름을 정확히 이해해야 오류를 줄일 수 있음을 확인했습니다.분기 주소 계산이 미흡하면 예상치 못한 동작이 발생하므로, 어셈블리어 작성 시 라벨 관리가 중요함을 체감했습니다. 🔗 컴퓨터 구조 미션3 블로그 링크회고잘한 점단계적 접근: 컴퓨터 구조와 알고리즘 미션을 작은 단위로 나눠 핵심 로직과 흐름을 차근차근 구현하고 검증했습니다.디버깅 습관 강화: Logisim 시뮬레이션과 코드 실행 결과를 반복 확인하며, 문제 발생 시 신호 흐름이나 분기 주소 동작을 빠르게 점검했습니다.이론·실습 병행: 트라이, 그래프, 다익스트라 개념 학습 뒤 직접 구현 연습, 제어 유닛 명령어 설계 뒤 Logisim 실습, 허프만 코딩 이론 뒤 코드 작성으로 이론과 실습을 잘 연결했습니다. 아쉬웠던 점 & 보완테스트 부족 : 테스트가 부족해 다양한 상황을 확인하지 못했습니다. 다음에는 다양한 입력과 경계값을 더 많이 시도해 보겠습니다.성능 최적화 부족: 성능 최적화 연습이 부족했습니다. 시간이 된다면 구현했던 자료구조와 알고리즘을 재작성해보고, 다양한 시도를 통해 실행 시간을 비교해 보겠습니다. 마치며이번 주차에는 Trie, 그래프, 다익스트라 학습을 진행하고, 제어 장치 명령어 확장과 허프만 코딩 구현 미션을 병행하며 이론과 실습을 모두 경험했습니다. 단계별 실습을 통해 개념이 실제 코드나 회로로 연결되는 과정을 체감했고, 디버깅과 테스트 습관을 강화할 수 있었습니다. 다음주에는 추가 알고리즘 학습을 통해 더욱 탄탄한 이해를 쌓아가겠습니다. 감사합니다!

알고리즘 · 자료구조인프런워밍업클럽스터디CS전공지식자료구조컴퓨터구조발자국회고4기

이승환

[워밍업 클럽 4기 백엔드] 2주 차 발자국 👣

학습 내용 요약 Readable Code 코드 다듬기디테일한 부분까지 리팩토링하며 읽기 좋은 코드를 작성하는 방법을 배웠습니다.주석, 패키지를 이용한 맥락 제공, 변수와 메서드 배치 순서 등에 대해 고민하며 리팩토링 연습을 진행했습니다. 리팩토링 연습리팩토링에 필요한 지식을 갖춘 상태에서 StudyCafe 예제를 리팩토링해보며,실제로 적용하는 연습을 했습니다.Day7 미션을 수행하면서 리팩토링의 어려움과 중요성을 체감할 수 있었습니다. 기억하면 좋은 조언들능동적인 코드 읽기의 중요성을 느꼈습니다.특정 개념을 배운 뒤 그 개념만 맹신하거나 추종하지 말아야 한다는 점을 배웠습니다.좋은 리팩토링은 결국 경험에서 비롯된다는 사실을 알게됐습니다. 다양한 예제를 통해 경험을 쌓을 계획입니다. Practical Testing 테스트는 왜 필요할까?테스트가 필요한 이유를 예시를 통해 이해할 수 있었습니다. 단위 테스트수동 테스트와 자동 테스트를 비교하며, 테스트 코드의 중요성을 체감했습니다.테스트 케이스라는 개념을 통해, 케이스를 어떻게 나눌지 고민할 수 있는 시야를 조금이나마 갖게 됐습니다. TDDTDD의 핵심 가치를 배웠고, TDD를 도입하는 것 만으로도 관점에 변화가 생김을 알 수 있었습니다. 테스트는 []다.테스트도 읽기 좋아야 한다는 메시지가 인상 깊었습니다.결국 개발자는 팀으로 일하기 때문에 팀원들과의 협업이 수월하도록항상 읽기 좋은 코드 작성에 신경 써야 함을 배웠습니다.  학습 내용 회고 🌱성취StudyCafe 예제를 통해 리팩토링 경험을 쌓았습니다.코드 리뷰를 통해 부족한 점을 파악하고 개선할 수 있었습니다.오버 엔지니어링 여부를 고민하고 더 나은 방향이 없을지 생각할 수 있게 됐습니다.단위 테스트 작성법을 익혔습니다.TDD를 설명할 수 있게 됐습니다. 읽기 좋은 테스트 코드에 대해 고민할 수 있게 됐습니다. ⚠발견한 약점 및 보완이 필요한 부분출력 로직과 패스의 책임 분리를 인지하지 못했습니다.isFixedType(selectedPass)처럼 매개변수로 객체를 넘기면서도,해당 구조의 문제점을 인지하지 못했습니다.boolean 메서드명을 무조건 is로 시작하는 습관이 있었습니다.테스트 코드 작성이 아직 익숙하지 않습니다. 📄보완 계획출력 로직과 패스 예시를 통해, 책임 분리가 제대로 되었는지 끊임없이 의심하는 시야를 갖도록 노력하겠습니다.이상적으로는 좋은 개발자 분에게 꾸준히 코드 리뷰를 받아 시야를 확장해나가는 것이 가장 좋겠지만, 현실적으로 어려움이 있어 이를 보완할 방안을 고민 중입니다.selectedPass.isFixedType()처럼 객체에게 묻는 방식에 익숙해지기 위해, 객체 중심 사고를 염두에 두고 리팩토링을 연습할 계획입니다.boolean 메서드라고 해서 무조건 is로 시작하기보다는, 자동사/타동사의 구분을 통해 명확한 이름을 짓는 연습을 하겠습니다. 헷갈리는 경우에는 검색을 통해 확인하며 감각을 익힐 예정입니다.TDD를 바로 시도하기보다는, 먼저 테스트 코드 작성 자체에 익숙해지는 것을 목표로 하고 있습니다. 이를 위해, Readable Code 예제로 단위 테스트를 연습할 계획입니다.(Day11 미션은 둘 중 하나만 골라서 단위 테스트를 작성하면 되지만, 저는 둘 다 해볼 예정입니다!)  미션 내용 회고 Day7 미션 🌱 🏃첫 번째 미션[섹션 7. 리팩토링 연습]의 "연습 프로젝트 소개" 강의를 보고, '스터디 카페 이용권 선택 시스템' 프로젝트에서 지금까지 배운 내용을 기반으로 리팩토링을 진행해 봅시다. 🔗작업 브랜치 링크https://github.com/sonic8-8/readable-code/tree/day7-mission 미션 과정어떤 클래스부터 접근할지, 리팩토링의 최적 순서가 있는지, 어떤 기준을 세워야 좋을지 등 많은 고민이 있었습니다.책임과 응집도, 적절한 메서드명까지 생각하다 보니 점점 복잡해지고 혼란도 생겼습니다.하지만 강의에서 제시된 키워드를 따라가며 하나씩 풀어 나가니, 리팩토링을 차근차근 시도해볼 수 있었고 좋은 훈련이 되었습니다.처음엔 낯설고 어려웠지만, 혼자 시도해보는 경험 자체가 큰 배움이 되었습니다. 리팩토링하며 사고한 모든 과정은 Notion에 정리해 두었습니다.(부끄럽지만 리팩토링하며 혼란스러워하는 흔적도 곳곳에 남아있습니다 😅) 리팩토링 사고 과정🔗https://able-brick-82b.notion.site/Day-7-studycafe-2062947a96118052949be0ae38983e68?source=copy_link 느낀점읽기 쉬운 코드를 작성하는 것 자체가 생각보다 훨씬 어렵다는 사실을 느꼈습니다.더 좋은 표현과 적절한 동사를 익히기 위해서는 결국 많이 접해보고 직접 써보는 수밖에 없다는 것도 알게 되었습니다.만약 더 디테일한 코드 리뷰를 받을 수 있는 기회가 있다면 적극 참여하고 싶다는 생각이 들었습니다.리팩토링은 결국 경험을 통해 길러야하는 영역이기에, 더 많은 예제를 통해 리팩토링을 연습할 계획입니다.충분히 연습한 후에는 지금의 코드와 사고 과정을 돌아보며 스스로 피드백도 해보고 싶습니다. 함께 스터디에 참여하시는 분들 덕분에 많은 동기부여를 받았고, 의지도 더 다져지는 것 같습니다.모두 완주까지 힘내봐요! 🏃 마지막으로 코드 리뷰를 해주신 우빈 코치님께 감사드립니다!온라인 세션을 통해 궁금했던 점과 고민을 해결하고, 부족한 점을 돌아볼 수 있는 정말 뜻깊은 시간이었습니다.귀중한 시간을 내어 코드 리뷰 해 주셔서 진심으로 감사합니다! 🙂‍↕ 강의🔗Readable Code: 읽기 좋은 코드를 작성하는 사고법🔗Practical Testing: 실용적인 테스트 가이드

백엔드워밍업클럽백엔드발자국

하얀종이개발자

인프런 워밍업 스터디 클럽 4기 데브옵스 2주차 발자국

2주차 요약 정리 Probe: 어플리케이션 생명주기를 반영하는 헬스체크Startup Probe: 앱이 완전히 기동되기 전까지의 초기화 확인.Readiness Probe: 트래픽 수신 가능 여부 확인 (연결/해제).Liveness Probe: 앱이 살아있는지 주기적 확인 (장애시 재기동).실습을 통해 실제 로그 흐름과 API 응답 시점, Probe 실패 → 재기동 상황까지 체감함.일시적 장애에 대한 Probe 설정 전략 (readiness만 실패 허용, liveness는 재기동 지연 설정)이 중요 포인트. ConfigMap & Secret: 설정과 민감 정보를 외부에서 주입ConfigMap: 환경 변수, 실행 설정 등 일반 설정 값 주입.Secret: Base64 인코딩 기반 민감 정보 전달. 실질적 보안은 RBAC/암호화와 병행 필요.마운트 방식에 따라 실시간 반영 가능 여부 달라짐 → envFrom vs. volumeMount 비교 실습.설정 변경 반영에는 Pod 재기동이 필요하다는 사실 확인. PV / PVC: 컨테이너 외부로의 데이터 영속화PV (영구 볼륨): 실제 물리 스토리지와 연결된 인프라 측 설정.PVC (볼륨 클레임): 앱에서 요청하는 인터페이스.local + nodeAffinity, hostPath 방식 실습 → 운영 환경에서의 주의점 인식.볼륨 설계는 앱 구조뿐 아니라 운영 자동화 전략과 직결됨을 이해. Deployment / Service / HPADeploymentRecreate: 서비스 중단 있지만 간결함.RollingUpdate: 중단 없이 점진적 배포. maxSurge, maxUnavailable로 유연하게 조정.실무에선 서비스 특성 + 자원 고려로 전략 선택 필요.ServiceClusterIP / NodePort → 내부/외부 접근.Service Discovery / Registry / Load Balancing 모두 포함된 멀티 기능.타겟 포트와 이름 기반 라우팅 실습으로 구조적 이해 확장.HPA (Auto Scaling)CPU 기반 스케일링 → 평균 사용률이 핵심.behavior 설정으로 불필요한 스케일링 방지.CPU 외의 지표 (Queue, Thread, DB 커넥션 등)에 대한 관찰 중요. 🪵 발자국 2주차 회고 💡 배운 점Probe를 통해 앱 상태의 생애주기를 쿠버네티스가 어떻게 자동화하는지 이해함.ConfigMap/Secret의 주입 방식, 실시간 반영 여부의 차이를 실습으로 체감.PVC/PV는 단순 저장공간이 아니라 운영 설계 철학과도 연결되어 있음.RollingUpdate 전략을 조절하며 중단 없는 배포와 자원 최적화의 균형을 고민하게 됨.HPA는 “자동”이지만 “만능”은 아님 → 스케일링의 현실을 정확히 바라보는 시야가 열림. 🤷‍♂ 잘한 점각 오브젝트를 앱 관점에서 재정의하며, 도구가 아닌 개념으로 이해하려 함.실습 로그와 API 응답 시점을 주의 깊게 분석하며 실제 운영 흐름을 그려봄.“기능”보다 “왜 그런 설계가 되었는가”에 집중해 구조적 통찰을 넓힘. ⚠ 아쉬운 점Probe 실패 시 앱 장애 로그 해석에 익숙하지 않아 시간이 걸림.HPA behavior 설정이 실제 환경에선 어떻게 적용될지 아직 가늠이 부족함.PVC 설정 시 nodeAffinity 이해에 다소 시간이 소요됨. 🎯 다음 주 목표쿠버네티스 전체 개념 복습하기Ingress 구조확인 & CI/CD 서버환경 구성하기 배포 파이프라인 구성하기 ☺한마디이번 주는 쿠버네티스를 단지 “컨테이너 자동화 플랫폼”이 아니라, 애플리케이션 운영의 철학과 구조를 담은 생태계로 느끼기 시작한 시간이었다.쿠버네티스를 알면 알수록, 단순한 툴을 넘어 사고방식의 변화가 필요하다는 걸 실감하고 있다.실수해도 괜찮다. 중요한 건 이 구조 안에서 직접 시행착오를 겪고 내 앱의 주인이 되는 것이다.다음 주에도 더 깊이 파고들어 보자.“기능을 배우는 게 아니라, 원리를 꿰뚫는 눈을 기르는 것”이 목표다.파이팅입니다 🔥💻🚀

백엔드dev-ops일프로인프런워밍업클럽데브옵스발자국

[워밍업 클럽 4기] 2주차 발자국 남기기

핑계지만 퇴근 후 시간만 할애해서 완강을 하기엔 너무 힘들 것 같다하지만 강의가 재밌어서 출퇴근길에도, 퇴근 후에도, 주말에도 몰입하여 진도를 최대한 따라가려 노력 중이다.클린코드에 대해서 뒤늦게 안 내가 바보같지만 지금이라도 알게되어서 다행이라고 생각한다. 강사님 정말 감사합니다. 이해가 쏙쏙 됩니다. 강제적으로 공부하게 해준 인프런, 언제나 최고라고 생각합니다.   Readable Code지난 주에 멈춰있던 나라 이번주에는 질주했어야 했다. 추상과 객체지향이 가장 중요한 키워드라고 생각한다.나와 동료 개발자, 후대의 개발자를 위해 클린코드를 지향해야 하고 그러기 위해서는 도메인에 대한 이해가 선수되어야 하며 이를 바탕으로 중요 정보를 가려내어 추상할 수 있어야 한다.추상의 과정에서 SOLID, 객체지향의 원칙들을 지키며 작성해 나가고 사고의 깊이를 줄이는 방식으로 코드를 작성하여 인지적 경제성이 좋은 방향으로 나아가야 한다. 리팩토링을 거듭하다보니 테스트 코드가 너무 필요하다 느껴진다. 다음 강의도 열심히 들어야겠다는 마음 가짐을 단단히 심어주시는 것 같다. Day 7 미션리팩토링할 곳이 많이 보였지만 시간에 급급하여 할 수 있는 것들만 진행하였다.개발자의 상상력을 동원해 오버 엔지니어링 되지는 않았는지 체크하는 게 가장 어려웠다.

백엔드워밍업클럽4기발자국클린코드

[ 인프런 워밍업 클럽 4기 Devops ] 1 주차 발자국(13일의 금요일 -안상민)

강의 수강 내용월 - OT, Q & A화 - Sprint1 컨테이너 한방 정리수 - 무게감 있게 쿠버네티스 설치목 - 실무에서 느껴 본 쿠버네티스 정말 평한 이유금 - Object를 그려보며 쿠버네티스 이해 강의 칭찬하고 싶은 점칭찬하고 싶었던점 헤이해졌었지만 다시 마음 잡아 공부를 시작한거 복습 글을 모두 작성했던 것 칭찬하고 싶습니다.아쉬웠던 점필기로 그려봐서 나의 것으로 확실히 만들려 했는데 글이나 그림 그리는데 시간이 부족해서 다 하지 못했습니다.그리고 시간이 상당히 오래 걸려서 비효율적이였습니다.보안해야 될 점쓸데 없는데 힘 빼지 말고(그림이나 글 직접 그리거나 쓰기) 공부하는 시간 딱 집중해서 수업을 듣고 복습 내용 바로 정리 해서 글 올리기, 발자국을 일주일 다되서 작성하지 말고 복습 그날그날 하면서 회고 때 넣을 내용 생각날 때 적어두기(생각 이 안남) 미션 미션 해결 하는 과정VM & K8S 설치 스크립트 도중에 종료처음에 스크립트가 시간이 꽤나 오래 걸리는지 모르고 렉이 걸려서 안되는지 알았다. ctrl + c 로 중지하게 되었다.그랬더니 남아 있는 베이그란트 프로세스가 컴퓨터 관리자에 남아 있어서 vagrant up 명령이 듣지를 않았다.검색 후에 돌아가고 있는 vagrant 프로세스를 다 종료, VM 에 남아 있는 모든 vagrant 관련 디렉터리를 삭제 후에다시 설치 했더니 문제가 해결이 되었다. 프로메테우스 모니터링 보드 오류프로메테우스 모니터링 보드에 모든 데이터가 No data 로 되어 있어서 Locky 가 k8s 클러스터 상의 컴포넌트들의 메트릭스를 수집 못하고 있나해서 Locky를 삭제 후 다시 깔았다. But 증상은 똑같았고 프로메테우스와 Locky를 모두 삭제 후 다시 설치 했더니 정상적으로 K8s의 파드 디폴로이 먼트 등 컴포넌트들의 모니터링이 가능해졌다.  

k8s회고발자국컨테이너VMk8s클러스터설치

인프런 워밍업 클럽 4기 DevOps - 2주차 발자국

전체적으로 느낀 점Kubernetes는 단순히 "컨테이너를 관리하는 도구"가 아니라, 전체 인프라의 상태를 코드로 정의하고 자동화하는 시스템이라는 걸 알게 되었다. 복잡하게 보였던 구성 요소들이 실제로는 잘 분리된 역할을 가지고 있다는 점이 인상 깊었고, 배울수록 실전에서 유용하겠다는 확신이 들었다. 배운 내용 요약 컨트롤 플레인과 클러스터 구조Control Plane은 클러스터의 두뇌로, 모든 오브젝트를 정의하고 감시하고 조율한다.etcd는 상태 저장소, Scheduler는 파드를 노드에 배치, Controller Manager는 상태를 지속적으로 유지.워커 노드는 kubelet, kube-proxy, containerd 등의 구성 요소로 작동하며, 컨테이너 실행은 Kubernetes가 직접 하는 게 아니라 런타임에게 요청한다는 구조가 흥미로웠다. 오브젝트 vs 컨트롤러오브젝트는 Pod, Service, PVC, ConfigMap처럼 기능 단위의 리소스컨트롤러는 이들을 자동화·유지·복구하는 역할 (Deployment, HPA 등)두 개념을 명확히 나눈 덕분에 Kubernetes의 확장성과 유연성이 보였다. ConfigMap과 SecretConfigMap: 설정값을 파드 외부에서 관리 → 환경별로 유연하게 구성 가능Secret: 민감정보 저장 (Base64 인코딩, 메모리 마운트)특히 Secret이 노드 메모리에 저장되고, 수정 시 kubelet이 주기적으로 반영하는 구조는 실제 운영환경에서 고려할 보안 요소가 많다는 걸 보여줌 Service서비스는 파드 IP가 변해도 고정된 접근점을 제공하는 추상화 계층ClusterIP, NodePort, LoadBalancer, ExternalName 타입마다 용도가 다르고,내부 DNS 기반의 이름 접근(서비스 디스커버리)은 마이크로서비스 아키텍처의 핵심 Probe (Liveness, Readiness, Startup)컨테이너가 살아있는지(Liveness), 준비됐는지(Readiness), 시작 중인지(Startup) 체크 가능Kubernetes가 단순 배포 도구가 아닌, 운영과 회복 기능까지 내장하고 있다는 걸 실감함설정에 따라 서비스 유입 제한, 자동 재시작 등 운영자가 손 쓸 필요 없는 자동화 처리가 가능 HPA (Horizontal Pod Autoscaler)설정된 기준(CPU 등)에 따라 파드 수를 자동으로 조절이상적인 스케일링과 현실적인 지연 시나리오를 비교하면서, HPA는 전능한 도구가 아니라 보조적인 수단임을 인지하게 됨 

데브옵스 · 인프라인프런워밍업클럽4기발자국DevOps

채널톡 아이콘