🎯 2025 완강 챌린지 🎯

블로그

양성빈

[인프런 워밍업 스터디 클럽] 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

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

양성빈

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

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

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

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미션과제발자국

양성빈

[인프런 워밍업 스터디 클럽] 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 개념을 도입하고 단일책임의 원칙을 적용하였으며 마지막에는 팩토리 디자인패턴과 테스트 코드로 마무리하였다. 📖 학습 방법 및 반성할 점정말 이번 미션이 나를 반성하게 하는 점이였다. 현업을 뛰는 나로서 현업(프론트엔드)에서 내가 얼마나 더러운 코드를 짰다는 생각이 많이 들었다. 그 동안 나는 쓰레기를 생산했다고 할 정도로... 그래서 나는 다음주 출근하자마자 시간이 된다면 바로 리팩토링 작업을 시작해야겠다고 느끼게 된 하루였다.회고이번주부터 정말 정신이 없었다. 직장다니면서 끝나자마자 회사 근처 카페에 가서 미션 수행하고, 정말 정신이 없었다. 심지어 어느 하루는 날밤을 세서 한 적도 있었다. 하지만 오히려 힘들고 불행했다기 보단 행복했다. 다른 분들은 이상하게 느낄 지 모르지만 뭔가 해결했다는 쾌감이 정말 감명 깊었고 지금의 마인드를 기억하면 다음주도 화이팅해서 성장해보겠다.

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

suover

인프런 워밍업 클럽 스터디 2기 - 백엔드 프로젝트 4주차 발자국

입문자를 위한 Spring Boot with Kotlin - 나만의 포트폴리오 사이트 만들기강의와 함께한 인프런 워밍업 클럽 스터디 2기 - 백엔드 프로젝트 (Kotlin, Spring Boot) 4주차 발자국 입니다. 강의 수강이번 주에는 Spring Boot와 JPA를 사용한 뷰 개발 및 Thymeleaf를 활용한 프론트엔드 템플릿 작업에 중점을 두었습니다. 주로 Bootstrap 템플릿을 가져와 활용하고, Thymeleaf의 fragment 기능을 사용하여 재사용 가능한 HTML 구조를 만들었으며, 배포 작업까지 진행했습니다.주요 학습 내용Bootstrap 템플릿 가져오기 및 적용Bootstrapmade 사이트에서 Admin 템플릿을 다운로드하여 프로젝트에 적용했습니다.템플릿 파일을 프로젝트의 resources 디렉토리에 추가하고, 필요한 CSS, JS 파일들을 적용했습니다.Thymeleaf와 프론트엔드 분리 작업Thymeleaf의 fragment 기능을 활용해 HTML 구조를 모듈화하였습니다. head, header, footer 등 여러 공통 요소를 별도의 fragment로 분리하여 재사용성을 높였습니다.타임리프 문법을 통해 동적으로 페이지를 구성하고 유지보수가 용이하도록 개선했습니다.프로젝트 배포 작업Docker를 이용해 MySQL을 컨테이너로 실행하고, Spring Boot 애플리케이션을 Docker 이미지로 빌드하여 배포했습니다.Nginx를 사용해 80포트와 8080포트를 연결하여 클라이언트 요청을 처리하도록 설정했습니다.Let's Encrypt를 이용해 HTTPS 인증서를 설정하고, 웹사이트를 HTTPS로 안전하게 접근할 수 있도록 구성했습니다. 회고이번 주는 Thymeleaf와 Bootstrap을 활용하여 프론트엔드 작업을 진행하며, 뷰를 모듈화하고 효율적으로 구성하는 방법을 배울 수 있었습니다. 특히 Thymeleaf의 fragment 기능은 HTML 코드의 재사용성과 유지보수성을 크게 개선해 주어 프로젝트의 구조를 더욱 체계적으로 만들 수 있었습니다. 또한, 직접 템플릿을 가져와 수정하고 적용하는 과정을 통해 프론트엔드 개발 역량을 키울 수 있었고, Docker와 Nginx를 이용한 배포 작업을 통해 실제 서비스 환경에서의 배포 경험도 쌓을 수 있었습니다.칭찬할 점Thymeleaf를 사용하여 공통 레이아웃을 fragment로 분리하여 재사용성을 높인 점.Bootstrap 템플릿을 프로젝트에 무리 없이 적용하고, 필요한 수정 작업을 성공적으로 수행한 점.Docker와 Nginx를 이용한 배포 과정을 원활히 진행한 점.아쉬웠던 점 및 보완할 점Thymeleaf 문법에 대한 익숙함이 아직 부족하여 일부 동적 작업에서 어려움을 겪었습니다. 추가적인 연습과 학습을 통해 더 익숙해질 필요가 있습니다.프론트엔드 요소의 커스터마이징에 있어 부족함을 느꼈으며, 좀 더 세밀한 스타일 수정 방법을 연구하고자 합니다. 미션이번 주 미션은 삽입, 수정, 삭제 REST API를 개발하고, 이를 검증하는 테스트 코드를 작성하는 것이었습니다. API는 각 기능별로 POST, PUT, DELETE 요청을 처리하도록 설계하였으며, 모든 케이스에 대해 성공적으로 테스트를 완료했습니다.미션 과정게시글 삽입 API 설계 (POST /api/posts)새로운 게시글을 작성하고, 작성된 데이터를 JSON 형태로 반환하도록 설계했습니다.게시글 수정 API 설계 (PUT /api/posts/{postId})특정 게시글을 수정하고, 수정된 결과를 반환하는 기능을 구현했습니다.게시글 삭제 API 설계 (DELETE /api/posts/{postId})특정 게시글을 삭제하고, 삭제 후 상태 코드 204 No Content를 반환하도록 했습니다.테스트 코드 작성 및 검증각 엔드포인트에 대해 최소 3개의 테스트 케이스를 작성해, 다양한 상황에서의 시스템 동작을 검증했습니다.필수 값이 누락된 경우, 잘못된 ID로 요청했을 때의 예외 처리 등도 함께 테스트하여 안정성을 확보했습니다.  미션 과정추가적으로 가상 프로필을 나의 프로필로 바꾸기 미션과 배포한 프로젝트 공유하기 미션까지 모두 마무리하며, 스터디의 모든 미션을 마무리 하였습니다. 이 발자국을 끝으로 모든 미션과 발자국, 강의 수강 100% 까지 마무리 하며, 스터디를 성공적으로 완주하였습니다. 회고이번 미션을 통해 RESTful한 API 설계 및 테스트의 중요성을 다시 한번 체감할 수 있었습니다. 또한, Thymeleaf를 활용해 프론트엔드를 모듈화하고 코드의 재사용성을 높이는 작업이 실제 프로젝트에서 어떻게 활용될 수 있는지 직접 경험할 수 있는 시간이었습니다. 배포 작업을 통해 실제 서비스 환경에서 발생할 수 있는 문제들을 다루며, 더 나은 품질의 소프트웨어를 개발하기 위한 실무 경험을 쌓을 수 있었습니다. 앞으로 JPA 연관 관계에 대한 이해를 깊이 있게 다지고, 테스트 코드의 가독성을 더욱 높이는 방법을 연구하여 더 나은 품질의 소프트웨어를 개발해 나가겠습니다.

백엔드인프런워밍업클럽스터디백엔드프로젝트발자국회고SpringBoot

양성빈

인프런 워밍업 스터디 클럽 2기 백엔드(클린코드, 테스트코드) 4주차 발자국

이 블로그 글은 박우빈님의 인프런 강의를 참조하여 작성한 글입니다.드디어 마지막 발자국 작성차례이다. 어느덧 벌써 수료날짜가 얼마 남지 않았다. 남은 시간 끝까지 달려보자. Presentation Layer 테스트외부세계의 요청을 가장 먼저 받는 계층파라미터에 대한 최소한의 검증을 수행MockMVCMock 객체를 사용해 스프링 MVC 동작을 재현할 수 있는 테스트 프레임워크요구사항관리자 페이지에서 신규상품을 등록할 수 있다.상품명, 상품타입, 판매상태, 가격등을 입력받는다. 그래서 해당 요구사항으로 비즈니스 로직을 작성 후 해당 부분 테스트를 해보았다. 여기서 잠깐 주목할만한 부분이라면 바로 @Transactional(readOnly = true)와 @Transactional이다. readOnly옵션은 읽기 전용이라는 뜻이다. 해당옵션에서는 CRUD중에 R만 기능동작을 하게 한다. 또한 JPA에서 CUD스냅샷 저장과 변경감지를 하지 않아 성능향상에 이점을 줄 수 있다. 또한 CQRS에서 Command부분과 Read부분을 나누자는 것처럼 우리의 비즈니스 로직중에 해당 클래스를 readOnly옵션을 전체로 주고 CUD에 해당되는 부분만 @Transactional을 사용하자!또한 우리는 validation을 적용하고 예외를 처리하기 위해 spring-boot-starter-validation 의존성을 추가해주고 각 dto에 어노테이션들을 추가해주었다. 또한 각 예외상황에 맞게 처리할 RestControllerAdvice를 두었으며 공통응답객체를 만들어 진행을 해보았다. 여기서 유심히 볼 부분이 몇가지 존재한다.⚠ @NotBlank vs @NotNull vs @NotEmptyNotNull은 null값을 허용을 하지 않는 것이고 NotEmpty는 ""문자열만 허용을 하지 않으며 NotBlank는 이 둘을 다 포함하면서 " "문자열도 포함시키지 않는다.또한 해당 request DTO를 만들면서 하나의 DTO를 이용해서 presentation layer부터 Business Layer까지 쓰이곤 한다. 이런 점은 DTO를 두고 의존성을 둘 수 있다는 것이다. 이건 layered architecture에 어긋한다. 따라서 서비스용 DTO, 컨트롤용 DTO를 별도 개발해서 진행을 해보는 작업을 해보았다. 또한 컨트롤러에서는 최소한의 검증을 하고 그 외에는 서비스용으로 옮겨보는 법도 생각해보았다. 미션이번 미션은 레이어 아키텍쳐에 관하여 설명과 테스트 방법에 대해 나만의 언어로 풀어쓰는 미션이다. 처음에는 내 언어로 표현한다는게 막막했지만 차근차근 정리를 해가다보니 금방 쉽게 풀어써졌다.미션링크 Mockito로 Stubbing하기요구사항현재 관리자 페이지에서 당일 매출에 대한 내역을 메일전송을 받고 싶어한다.그래서 우리는 해당 요구사항을 바탕으로 비즈니스 로직을 작성하였다. 핵심은 테스트이다. 메일 전송은 외부 네트워크를 타고 전송이 되는 로직이라 매우 오랜 시간이 걸린다.(내 경험) 또한 메일을 계속 보내다보면 나중에 수 많은 테스트 메일이 쌓여 있는 것을 알 수 있다. 이럴때는 어떻게 할까? 메일 전송 행위자체를 mocking하면 되는데 이것을 stubbing이라고 한다. 우리는 메일 전송 로직을 mockBean을 이용해 mocking하고 행위 자체를 stubbing하여 테스트를 수행 할 수 있었다. Test Double출처배우 대역의 스턴트맨이 그 Test Double의 근원지이다.관련 용어Dummy: 아무것도 하지 않는 깡통객체Fake: 단순한 형태로 동일한 기능은 수행하나 프로덕션에서 쓰기에는 부족한 객체(ex. FakeRepository)Stub: 테스트에서 요청한 것에 대해 미리 준비한 결과를 제공하는 객체. 그 외에는 응답XSpy: Stub이면서 호출된 내용을 기록하며 보여줄 수 있는 객체. 일부는 실제 객체러첨 동작시키고 일부만 stubbingMock: 행위에 대한 기대를 명세하고 그에 따라 동작하도록 만들어진 객체 Stub과 Mock을 헷갈려한다. 동일한거 아닌가? 결론부터 말하자면 Mock은 Stub이 아니다. Mock은 행위에 대한 검증을 할때 사용하고 Stub은 상태에 대한 검증을 할때 사용된다. @Mock, @Spy, @InjectMocks순수 Mockito로 검증하는 시간을 가져보았다. 우리는 @SpringBootTest를 붙여서 통합 테스트를 가져보았는데 그렇게 하지 않고 순수 Mockito로 테스트할 수 있는 방법은 없을까?그래서 우리는 기존에 MailService를 테스트를 가져보았다. 의존성으로 주입된 것들을 mock()을 이용하여 만들어주고 MailService를 해당 mock()으로 만든 것을 생성자에 넣어주어서 실행했다.하지만 이렇게 하는것보다 더 좋은 방법은 @Mock과 @InjectMocks를 이용하는 방법이다. mock()으로 생성한 객체는 @Mock 어노테이션을 붙여서 사용이 가능하고 만들어진 mock 객체를 이용하여 생성한 객체 MockService는 @InjectMocks를 이용하여 더 간단히 사용이 가능하다. 추가적으로 해당 메서드 안에 특정메서드가 몇번 불렸는지 확인하는 것은 verify로 확인이 가능하다.@Mock을 사용할 때는 @ExtendWith(MockitoExtension.class)을 붙여줘야 한다.또한 @Spy를 이용할건데 사용법은 @Mock과 유사하다. 다른점은 @Mock을 사용할때 when().thenReturn()식을 이용했는데 @Spy는 doReturn().when().메서드()를 이용하면 된다. @Mock과 비교해보자. @Mock을 사용하면 해당 메서드만 mocking을 하여 사용하고 그 외의 메서드들은 작동을 안 한다. 하지만 @Spy를 사용하면 해당 객체에 호출한 메서드들이 전부 작동이 된다. 그래서 일반적으로 @Mock을 사용하지만 @Spy를 이용할 때도 있으니 알아두자. BDDMockitoBDD는 행위주도개발로 이전에 한번 살펴본 적이 있다. 이전에 실습한 코드를 보면 뭔가 이상하다고 느낄 수 있을 것이다.// given when(mailSendClient.sendEmail(anyString(), anyString(), anyString(), anyString())).thenReturn(true);분명 given절인데 when이 들어가져 있다. 그래서 만들어진 것이 BDDMockito다. BDDMockito의 given을 이용하면 아래와 같이 변경이 가능하다.given(mailSendClient.sendEmail(anyString(), anyString(), anyString(), anyString())).willReturn(true);그럼 정확히 given에 given을 이용하니 명확해진다. Classicist VS. MockistClassicist입장은 다 mocking하면 실제 객체들의 동작 및 두 객체들이 합쳐서 동작할때 그에 따른 사이드 이펙트는 어떻게 검증할건데라는 입장이며 Mockist 입장은 모든 걸 다 mocking하면 각 객체들의 기능에 대한걸 보장하니 굳이 통합테스트를 할 필요가 없다라는 입장이다.우리는 Persistence Layer에서 단위 테스트를 진행하고 Business Layer에서는 통합 테스트를 Presentation Layer에서 두 레이어를 모킹하여 진행했다. 이것을 Mockist가 본다면 굳이 시간낭비하고 있다고 할 것이다.우빈님의 생각은 Classicist입장이시며 mocking을 할때는 외부시스템을 사용할 일이 있을때 사용한다고 하셨다.즉 결론은 mocking을 했다 하더라도 실제 프로덕션 코드에서 런타임 시점에 일어날 일을 정확하게 stubbing했다 단언할 수 없으니 통합테스트를 해보자는 것 같다.나의 입장은 이렇다. QuerDSL같은 외부자원을 사용할때 나는 해당 부분을 테스트할때 테스트를 진행하고 비즈니스 레이어에는 해당 부분을 mocking처리한다. 그리고 컨트롤러 부분에서 통합테스트를 사용하는데 이 부분은 한번 질문을 드려봐야겠다. 한 문단에 한 주제이전 강의인 Readable Code에서도 배웠고 학창시절 영어독해시간에도 배웠듯이 한 문단에는 하나의 주제로 이루어져야 한다. 테스트도 문서다. 즉, 각각 하나의 테스트는 하나의 문단이고 그 테스트 코드는 하나의 주제를 가져야 한다. 완벽하게 제어하기테스트 코드를 작성할 때는 모든 조건들을 완벽히 제어가 가능해야 한다. 우리는 이전에 LocalDateTime.now()을 사용하면서 현재시간에 따라 테스트가 성공할때도 있고 실패할때도 있는 경우를 보았다. 그래서 우리는 이를 해결하기 위해 이 현재시간을 상위에 넘기고 파라미터로 받는 방식을 택하였다.그러면 만약에 LocalDateTime.now()를 사용해도 테스트가 무조건 성공한다면 사용해도 되나? 우빈님께서는 지양한다고 하셨다. 그 이유는 그 테스트는 성공할진 몰라도 팀 단위에서 해당 코드를 사용하면 그게 프로젝트에 번져서 빈번히 사용할 확률이 높기때문이라고 하셨다. 나도 이점을 한번 주의해야겠다. 테스트 환경의 독립성을 보장하자테스트 환경은 대부분 given절에서 환경을 세팅한다. 그런데 이런 given절에서 다른 API를 사용하게 된다면 어떻게 될까? 해당 API와 테스트 코드가 결합도가 생기게 된다. 이런 부분을 방지하고 테스트코드 독립성을 보장시켜야 한다.@Test @DisplayName("재고가 부족한 상품으로 주문을 생성하려는 경우 예외가 발생한다.") void createOrderWithNoStock() {     // given     LocalDateTime registeredDateTime = LocalDateTime.now();     Product product1 = createProduct(BOTTLE, "001", 1000);     Product product2 = createProduct(BAKERY, "002", 3000);     Product product3 = createProduct(HANDMADE, "003", 5000);     productRepository.saveAll(List.of(product1, product2, product3));     Stock stock1 = Stock.create("001", 2);     Stock stock2 = Stock.create("002", 2);     stock1.deductQuantity(1); // @todo     stockRepository.saveAll(List.of(stock1, stock2));     OrderCreateServiceRequest request = OrderCreateServiceRequest.builder()             .productNumbers(List.of("001", "001", "002", "003"))             .build();     // when & then     assertThatThrownBy(() -> orderService.createOrder(request, registeredDateTime))             .isInstanceOf(IllegalArgumentException.class)             .hasMessage("재고가 부족한 상품이 있습니다."); }위의 코드에서 todo부분을 살펴보자. 해당 코드는 주문생성관련 로직이다. 그런데 deductQuantity를 현재 재고보다 많이 차감시키면 해당 메서드에서 예외를 던질 것이다. 그러면 given절에서 테스트가 실패되고 내가 위에서 말했던 결합도가 생긴 케이스이다. 또한 이건 내 생각이지만 주문생성로직에 재고차감로직이 들어가 있으니 하나의 테스트코드에 주제가 2개가 되버리는 상황이 생긴다. 이런것을 방지하는 것이 좋다. 테스트 간 독립성을 보장하자테스트는 각각 수행되어야 하고 테스트 순서가 무작위여도 같은 결과가 나와야 한다. 하지만 만약 테스트에 공유자원을 사용하게 되면 예상치 못한 결과가 나올 우려가 있기에 테스트가 실패할 경우도 있을 것이다. 이런 점때문에 공유자원 사용은 지양하자. 한 눈에 들어오는 Test Fixture 구성하기Test FixtureFixture: 고정 틀, 고정되어 있는 물체 (given절에 생성한 객체들)테스트를 위해 원하는 상태로 고정시킨 일련의 객체 우리는 이에 관련해서 @BeforeEach BeforeAll에 대해 알아보았다. 그런데 @BeforeEach같은 경우 given절에서 만든 데이터를 넣는 행위는 지양하다고 하셨다. 결국 이것은 이전에 봤던 공유자원과 같은 역할을 한다. 또한 테스트는 문서인데 given절을 보려니 없어서 스크롤로 위로 왔다갔다 하는 상황이 발생하기 때문에 테스트 문서의 파편화가 일어난다.그러면 언제 @BeforeEach를 쓸까? 각 테스트 입장에서 봤을 때 아예 몰라도 테스트 내용을 이해하는데 문제가 없는가?라는 물음과 수정해도 모든 테스트에 영향을 주지 않는가?라는 물음에 괜찮다면 사용하자! 그렇지 않다면 지양하자.또한 data.sql을 이용해 미리 쿼리를 생성해 given절을 작성하는 행위는 지양하자. 왜냐하면 테스트 파편화가 일어나기도 하고 나중에 실무에 가면 수십개의 컬럼과 수십개의 테이블이 있고 이 테이블이나 컬럼이 바뀔때마다 수정을 해줘야 하기때문에 관리포인트가 늘어나기 때문이다.또한 given절에 객체를 생성 시, 필요한 파라미터만 주입받는것을 고려하면 작성하자. 해당 파라미터를 보고 이 파라미터는 테스트에 전혀 영향을 주지 않을 것 같은 것은 고정값으로 두고 파라미터를 빼는 것처럼 말이다.마지막으로 builder와 같이 given절에 들어가는 것을 하나의 테스트 config 클래스에 모아두는 행위는 지양하자. 왜냐하면 나중에 실무에서 작성하다보면 새로운 빌더가 생기고 메서드 오버로딩때문에 파라미터 순서만 바뀌는 빌더도 많이 생길 수 있으며 테스트 문서 파편화로 인해 더 불편해질 것 같다. 그래서 클래스마다 필요한 파라미터만 받아서 작성하는 것이 좋다. Test Fixture 클렌징deleteAll()과 deleteAllInBatch()에 차이에 대해 알아보자. 우빈님은 deleteAllInBatch()를 더 선호하신다고 하셨다. 그 이유에 대해 알아보자.deleteAllInBatch()는 delete from~ 절만 나가는 순수 테이블 전체 삭제에 용이하다. 하자민 단점이라하면 순서를 잘 생각해야 한다. 만약 A라는 테이블과 B라는 테이블이 M:N 연관관계를 맺어 중간테이블 AB라는 테이블이 있을때 AB부터 테이블을 삭제해주고 A, B순서대로 삭제를 해줘야 한다. 그렇지 않으면 예외가 발생한다.deleteAll()은 굳이 중간테이블을 삭제할 필요없이 중간테이블을 알고 있는 테이블만 삭제해도 알아서 삭제해준다. 하지만 단점은 해당 메서드를 실행하면 해당 엔티티를 먼저 전체 select를 하고 다음으로 delete from where 절이 나간다. 그리고 해당 절은 테이블에 있는 데이터 수 만큼 나가기 때문에 수많은 데이터가 존재하면 성능이 매우 떨어질 것이다.그래서 deleteAllInBatch()가 더 선호하는 이유를 알 것 같다. 하지만 이런것보다 side effect를 잘 고려해서 @Transactional을 잘 사용하는 것이 베스트일 것 같다. @ParameterizedTest테스트 코드에 if-else나 for문같이 조건문, 반복문등 읽는 사람의 생각이 들어가는 것을 지양해야한다고 말했다. 그래서 여러가지 테스트 로직이 하나의 테스트 코드에 있다면 분리하자고 하였다. 그런데 만약 단순히 값 여러개로 하나의 테스트를 하고 싶은경우 테스트를 나누는 것보다 @ParameterizedTest를 사용하는 것이 깔끔해진다.대표적인 예시로 내가 사이드프로젝트에서 작성했던 코드를 들 수 있을 것 같다.@ParameterizedTest @MethodSource("providedTestDataForSignup") @DisplayName("회원가입 통합 테스트 - 실패(유효하지 않은 회원가입 폼)") void member_signup_integration_test_fail_caused_by_invalid_signup_form(String name, String nickname, String email, String password, String confirmPassword, String phoneNumber, LocalDate birthday) throws Exception {     MemberSignupRequestDto requestDto = new MemberSignupRequestDto(name, nickname, email, password, confirmPassword, phoneNumber, birthday);     this.mockMvc.perform(post("/api/auth/signup")                   .contentType(MediaType.APPLICATION_JSON + ";charset=UTF-8")                     .accept(MediaType.APPLICATION_JSON + ";charset=UTF-8")                     .content(this.objectMapper.writeValueAsString(requestDto)))             .andDo(print())             .andExpect(status().isBadRequest())             .andExpect(jsonPath("message").exists())             .andExpect(jsonPath("status").value(GlobalExceptionCode.INVALID_REQUEST_PARAMETER.getHttpStatus().name()))             .andExpect(jsonPath("code").value(GlobalExceptionCode.INVALID_REQUEST_PARAMETER.getCode()))             .andExpect(jsonPath("timestamp").exists()); } private static Stream<Arguments> providedTestDataForSignup() {     return Stream.of(             Arguments.of("양성빈", "tester", "email@email.com", "1q2w3e4r5t!", "1q2w3e4r5t!", "010-1234-1234", LocalDate.of(1999, 1, 1)),             Arguments.of("양성빈", "robert", "test@email.com", "1q2w3e4r5t!", "1q2w3e4r5t!", "010-1234-1234", LocalDate.of(1999, 1, 1)),             Arguments.of("양성빈", "robert", "email@email.com", "1q2w3e4r5t!", "t5r4e3w2q1@", "010-1234-1234", LocalDate.of(1999, 1, 1)),             Arguments.of("양성빈", "robert", "email@email.com", "1q2w3e4r5t!", "1q2w3e4r5t!", "010-1111-1111", LocalDate.of(1999, 1, 1))     ); }이렇게 @MethodSource를 사용하는 경우 이외에오 @CsvSource를 이용하는 경우도 있고 다른 방법도 있으니 한번 찾아보면 좋을 것 같다. @DynamicTest공유변수를 가지고 여러개의 테스트가 사용하는 것은 지양하자고 하였다. 왜냐하면 테스트의 순서가 생겨버리고 서로 강결합이 되기 때문이다. 하지만 환경 설정 후 시나리오 테스트를 하는 경우가 있을 것이다. 그럴 경우 @DynamicTest를 이용하자. 사용법은 아래와 같다.@TestFactory @DisplayName("") Collection<DynamicTest> dynamicTests() {     // given     return List.of(             DynamicTest.dynamicTest("", () -> {                 // given                 // when                 // then             }),             DynamicTest.dynamicTest("", () -> {                 // given                 // when                 // then             })         ); } 테스트 수행도 비용이다. 환경 통합하기이제 전체 테스트를 돌려보자. 지금 테스트를 전체 돌려보면 2.x초라는 시간이 걸리고 spring boot 서버가 6번 뜨는 불필요한 행위가 발생한다.테스트 코드를 작성하는 이유가 인간의 수동화된 검증에 대한 불신도 있지만 가장 큰 이유는 시간을 아끼기 위해서이다. 하지만 테스트를 돌렸는데 2.x초라는 행위는 너무 아깝다. 그래서 우리는 하나의 통합추상 클래스를 만들어 service와 repository부분을 하나의 추상클래스를 상속받게 함으로 테스트 띄우는 횟수를 줄였다. 그리거 마지막 controller부분도 별도의 추상클래스를 만들어 해당 클래스를 상속받게 함으로 서버 띄우는 횟수를 줄여갔다. 결론적으로 총 서버는 2번 띄워졌고 2.x초에서 1.x초로 속도가 줄어갔다. Q. private 메서드의 테스트는 어떻게 하나요?정답은 할 필요도 없고 하려고 해서도 안된다. 클라이언트 입장에서는 제공되는 public 메서드만 알 수 있고 알아야하며 그 외의 내부 메서드는 알 필요가 없다. 또한 public 메서드를 테스트한다는 것은 내부 private 메서드도 자동으로 같이 테스트하는 것이므로 따로 테스트를 할 필요가 없다. 다만 만약 이렇게 해도 계속 위의 물음이 생각나면 객체를 분리할 시점인가를 검토해야한다. 그리고 객체를 분리시켜 해당 private 메서드를 해당 객체의 public 메서드로 두고 단위 테스트를 진행해야 할 것이다.나는 미션을 진행하면서 이런 물음이 생각났고 private 메서드를 리플렉션을 통해 테스트를 한 경우가 있는데 위의 해답을 듣고 나니 괜히 테스트를 한것이구나라는 생각이 들어 조금은 반성하게 된 계기였다. Q. 테스트에서만 필요한 메서드가 생겼는데 프로덕션 코드에서는 필요 없다면?답변은 만들어도 된다. 다만 보수적으로 접근해야 한다. 즉, 어떤 객체가 마땅히 가져도 되고 미래지향적이면 만들어도 상관없다. 학습 테스트잘 모르는 기능, 라이브러리, 프레임워크를 학습하기 위해 작성하는 테스트여러 테스트 케이스를 스스로 정의하고 검증하는 과정을 통해 보다 구체적인 동작과 기능을 학습관련 문서만 읽는 것보다 훨씬 재밌게 학습  Spring REST Docs테스트 코드를 통한 API 문서 자동화 도구API 명세를 문서로 만들고 외부에 제공함으로써 현업을 원활하게 한다.기본적으로 AsciiDOC을 사용하여 문서를 작성한다. Spring REST Docs vs SwaggerSpring REST Docs장점테스트 코드를 통과해야 문서가 만들어진다. (신뢰도가 높다)프로적션 코드에 비침투적이다.단점코드 양이 많다.설정이 어렵다.Swagger장점적용이 쉽다문서에서 바로 API 호출을 수행해볼 수 있다.단점프로덕션 코드에 침투적이다.테스트와 무관하기 때문에 신뢰도가 떨어질 수 있다. 미션3 진행테스트코드의 마지막 미션을 진행하였다. 이번에 배운 어노테이션들, @BeforeEach가 적용하여 BDD 스타일 적용하는 실습등 미션을 진행했는데 해당 미션을 통해서 해당 어노테이션들에 대해 확실히 알 수 있었으며 BDD 스타일이 조금 적응된 것 같다.미션링크 깜짝 특강Day18 미션 공통 피드백핵심은 중복 제거가 아니고 도메인이다. 사용자, 게시물은 간접적이므로 setup()으로 댓글은 직접적이므로 given절에 배치해야 한다.Q&A잘문) REST API의 조건 중 하나인 hateoas에 대해 실무에서 많이 사용하는지와 제가 제대로 적용하고 있는지답변) 아직까지 사용한 곳을 본적이 없다고 하셨다. 그 이유를 생각해보시는데 단순히 hateoas를 적용하기에는 APP-FE-BE 간 긴밀한 스펙과 복잡한 동작들이 너무 많고 또 이미 만들어져 있는 구조를 hateoas 형태로 전환한다는것은 불가능에 가깝다고 느끼신다고 하셨다. 또한 실무에서는 대부분 그런 규칙을 지키는 것보다 다른 중요한 것들이 더 많다고 판단하기 때문이라고도 하셨다.코드리뷰우빈님께서 많은 칭찬을 해주셔서 몸둘 바를 모르겠지만 몇가지 피드백 사항이 있었다. 그 중에 제일 생각나는 피드백을 말하면 다음과 같다. given / when / then절에 설명하는 주석은 생략해도 좋다. 하지만 given절이 몇십줄이고 서로 다른 특징들이 나열되어 있다면 간단히 적는 걸 추천하신다고 하셨다.이것으로 모든 워밍업 클럽 진도가 완료가 되었다, 나 자신에게도 뜻 깊은 경험과 성장이 되었으며 이러한 경험의 반복을 이뤄나가고 싶다.

백엔드인프런워밍업스터디클럽2기백엔드클린코드테스트코드발자국

대협

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

이 블로그는 정보근님의 입문자를 위한 Spring Boot with Kotlin - 나만의 포트폴리오 사이트 만들기 강의 기반으로 코드작성과 코드설명을 적었습니다 나의 첫 인프런 블로그가 이렇게 쓰여질 줄은 몰랐다.. 그동안 인프런 강의들을 많이 애용했지만, 이렇게 블로그를 작성하지는 않아서 낯설기도 하고, 설레기도 한다.발자국 내용은 딱히 형식이 정해져 있지 않아서 발자국 쓰기 전날까지 들었던 강의들 중, 모르는 내용들을 적어서 내가 다시 봤을 때, "아 맞다!" 라는 말이 나올 정도로만 적을 예정이다! 목차모르는 내용 정리 미션 해결 과정회고   1. 모르는 내용 정리 <Gradle>Gradle : Gradle은 Groovy를 기반한 빌드 관리 도구.빌드 관리 도구 : 프로젝트 내에서 다양하게 외부 라이브러리와 로컬 레포지토리에서도 라이브러리 별로 버전 관리를 해야할 때 용이Gradle이 왜 필요할까?Gradle과 Maven 차이를 알면 Gradle이 왜 필요한지 알 수 있다.Gradlegroovy 언어를 사용한 Domain-specific-language를 사용 ( 코드가 간결)어느 task가 업데이트 되었는지 체크이미 반영된 빌드 부분은 더이상 재 실행되지 않는다. -> 빌드 시간 단축Mavenjava8용 프로젝트 관리 도구 apache의 ant 대안외부 저장소에서 필요한 라이브러리, 플러그인들을 다운로드 한 후 , 로컬 시스템의 캐시에 모두 저장그래서 왜 Gradle인데!!!Gradle은 작업 의존성을 그래프, Maven은 고정적이고 선형적 모델을 기반Gralde은 어떤 task가 업데이트되었고 안되었는지를 체크Gradle은 이미 업데이트 된 task에 대해서는 작업이 실행되지 않으므로 빌드 시간 단축빌드 설정 규모가 작으면 큰 차이를 느끼지 못하지만 규모가 크면 훨씬 Gradle의 빌드 시간이 단축된다는걸 알 수 있다. <Dependencies>Spring Web : 웹 어플리케이션을 개발하기 위한 핵심 기능 제공Thymeleaf : View Template, 동적으로 화면을 구성할 수 있게 해줌Spring Data JPA : JPA를 이요한 구현체를 더 추상화시켜 더 쉽고 간편하게 JPA를 이용 가능mysql driver : mysql를 연동할 때 필요한 dependencyh2 database : RDBMS, 테스트 단계 또는 작은 규모의 프로젝트에서 사용validation : 데이터에 대한 유효성 검증 처리를 수행Spring Security : 홈페이지에 인증 및 권한 기능을 빠르게 부여해 인증 및 권한 보호 기능을 손쉽게 추가할 수 있음. <프로그램이 시작되는 시작되는 코드>src/main/kotlin/PortfolioApplicatioin.ktfun main(args: Array<String>) { runApplication<PortfolioApplication>(*args) } Whitelabel Error Page 오류가 뜨는 이유 : 어플리케이션이 뭘 해야 할지 모르기 때문에 Whitelabel Error Page를 보내줌.(정상) <Git 용어>Git : 분산 버전 관리 시스템 ⇒ 협업을 쉽게 해주는 툴 : GitHubCommit : git에 저장하는 단계Rollback : 이력 되돌리기Branch : branch 생성 및 제거, 확인등의 기능을 하는 명령어Merge : 브랜치 병합Conflict : 충돌Repository : 원격 저장소 , 인터넷이나 네트워크 어딘가에 있는 저장소Push : 로컬 브랜치를 원격 저장소로 푸시할 때 사용하는 기본 명령어 <Git 명령어>git init : git 공간으로 초기화git status 명령어 입력시 Untracked files 라는 게 있는데 이건 git에서 따로 설정을 안한다는 소리https://www.toptal.com/developers/gitignore/ : gitignore 파일을 자동으로 만들어줌 <github에 PUSH 방법>터미널에서 git initgit remote “git 저장소”→ 인텔리제이(GUI)로 할 시에는 git → Manage Remotecommitpush <application-default.yml , application-docker.yml>profile이 default 이면, application-default.yml 파일에서 환경변수를 가져오고profile이 docker 이면, application-docker.yml 파일에서 환경변수를 가져온다. application-default.yml 파일에서 spring:jpa:hibernate:ddl-auto:create 인데왜 application-docker.yml 파일에서는 spring:jpa:hibernamte:ddl-auto:none 일까?=> application-default.yml 에서는 개발 목적으로 데이터베이스 스키마를 매번 재생성할 필요가 있지만, application-docker.yml 에서는 베포 및 운영 환경에 적합하게 데이터베이스 구조가 변경되지 않도록 하기 위한 설정 <어노테이션 정리>@Id : 해당 테이블의 PK 필드를 의미@Entity : JPA를 사용해 테이블과 매핑할 클래스에 붙여주는 어노테이션@GeneratedValue : JPA에서 Entity의 PK를 생성하여 주는 기능-> @GeneratedValue(name="member_id") : PK로 사용될 Entity의 프로퍼티에 @Id 어노테이션 선언-> @GeneratedValue(strategy=GenerationType.IDENTITY) : 기본 키 생성을 데이터베이스에 위임한다.-> @GeneratedValue(strategy=GenerationType.AUTO) : 기본값으로 DB 방언에 맞춰 자동으로 설정하여 준다.@Column : 객체 필드와 DB 테이블 칼럼을 맵핑한다.@Component : 클래스를 자동으로 빈으로 등록하기 위해 클래스 레벨에서 사용@Profile : 빈이나, 컴퓨넌트에게 프로필을 정해줄 수 있음@PostConstruct : 객체의 생성이 일어난 직 후에 초기화를 수행하는 메서드에 부착한다.@ManyToOne : 단방향 관계이고 FK관리에 있어서 가장 자연스럽다. @JoinColumn 어노테이션과 같이 쓰인다.@JoinColumn : 엔티티 테이블에 FK 칼럼을 정의해준다. 자료형뒤에 ? 는 무슨 의미일까 ?ex ) Long?자료형 뒤에 ? 가 오는 것은 Kotlin에서 사용되는 문법이고 ?를 자료형 뒤에 붙이는 방식은 nullable 여부를 나타낸다. 예를 들면 var name: String? = null 이 코드는 해당 변수 name에 null 값이 올 수도 있다는 뜻이다.  <코드 분석>var type: SkillType = SkillType.valueOf(type) : SkillType.valueOf(type) 은 type이라는 문자열을 SkillType 열거형의 값으로 변환. 만약 type이 열거형에 없는 값이면 예외 발생var cookies: String? = httpServletRequest.cookies ?.map {"${it.name}:${it.value}"} ?.toString() : httpServletRequest에서 쿠키 정보를 가져오고, 쿠키 이름과 값을 "이름:값" 형식으로 변환하는 코드var referer: String? = httpServletRequest.getHeader("referer") : HTTP 요청에서 "Referer" 헤더 값을 가져온다. 이 코드는 사용자가 어떤 페이지에서 현재 페이지로 이동했는지 나타냄@OneToManyvar details: MutableList<ExperienceDetail> = mutableListOf() : JPA 관계를 나타내고, 엔티티 간의 일대다(One-to-Many) 관계를 나타냄2. 미션미션 1과 2를 제출하는건데, 미션1은 테이블 설계하기와 깃허브 리포지토리에 프로젝트 올리기이다. 미션 1 : https://github.com/HyupTech/LMS/commit/fa47b404d36b3ce418f16213e3bb30ca96b812ed미션 2 : https://github.com/HyupTech/LMS/commit/0993897036a0e17e7a366031b950235edd5d506e   3. 회고발자국을 작성하면서 나는 이제까지 강의를 보면서 공부를 했지만 다시 한번 이렇게 정리를 해가면서 강의를 보지 않았다. 왜냐하면 시간이 너무 아까웠고, 차라리 정리하는 시간에 강의를 하나 더 보자는 마인드였다. 하지만 발자국을 써보면서 왜 이렇게 좋은걸 내가 안했을까라는 후회가 들고, 이렇게 정리를 해가면서 했으면 아마 실력이 조금이라도 더 올랐지 않았나 라는 생각이 들었다. 앞으로 발자국도 쓰고, 내가 따로 공부하고 있는것도 정리해가면서 공부를 해야겠다. 3-1. 미션 회고 이번 미션에서 처음으로 ERD를 구성하고 어디에 PK를 주고 관계 설정을 어떻게 할지에 대한 고민이 많았던 것 같다. 현재 대학교 2-2에 재학중인데 데이터베이스 과목을 수강중인데 꽤 도움이 되었던 것 같고, 백엔드 개발자가 될려면 데이터베이스 공부도 놓지 말아야겠다고 생각이 들었다. 앞으로 더 많은 미션들이 기다리고 있는데 열심히 공부를 해야겠다!   

백엔드인프런워밍업클럽백엔드발자국javakotlin

윤승현

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

강의 수강[운영체제]세그멘테이션 : 메모리를 서로 다른 크기의 논리적 단위인 세그먼트로 나누어, 각 세그먼트가 독립적인 주소 공간을 가지도록 관리하는 기법이다.페이징 : 메모리를 할당할 때 정해진 크기의 페이지로 나눈다.페이지드 세그멘테이션 : 세그멘테이션과 페이징을 혼합해 장점을 취한 방식이다. 디맨드 페이징 : 디맨드 페이징은 조만간 필요할 것 같은 데이터를 메모리로 가져오고 쓰이지 않을 것 같은 데이터는 스왑영역으로 이동시키는 정책이다.페이지 교체 정책 : 메모리에 있는 페이지를 스왑영역으로 옮길 때 어떤 페이지를 선택할지 결정하는 정책이다.스레싱 : CPU 사용률을 높이려 했지만 오히려 더 떨어지는 상황을 '스레싱'이라고 한다.워킹셋 : 메모리에 올라온 페이지는 다시 사용할 확률이 높기 때문에 하나의 세트로 묶어서 메모리에 올리는 것을 '워킹셋'이라고 한다. 그리고 프로세스가 준비상태에서 실행상태가 되는 컨텍스트 스위칭을 할 때 사용된다.주변 장치 : 그래픽 카드, 하드디스크, SSD, 키보드, 마우스 등 여러가지가 있다. 그리고 캐릭터 디바이스와 블록 디바이스 두 가지로 나눌 수 있다.마우스/키보드 : 사용자 입력 장치이다.하드디스크 : 기계식으로 저렴하다. 비휘발성 저장 장치이다.Flash Memory(SSD) : 전자식으로 빠르고 고가이다. 비휘발성 저장 장치이다.파일과 파일시스템 : 파일은 데이터를 저장하는 논리적 단위이며, 파일시스템은 이러한 파일들을 효율적으로 저장, 관리, 접근할 수 있도록 조직화하는 구조와 메커니즘입니다.디렉토리 : 파일들을 그룹화하고 계층적으로 조직하여 쉽게 관리하고 접근할 수 있도록 하는 파일 시스템 내의 구조이다.[자료구조와 알고리즘]삽입 정렬장점 : 이해와 구현이 간단하다.단점 : 성능이 O(n²)으로 성능이 좋지 않다.시간 복잡도 : O(n²) 병합 정렬장점 : 성능이 좋다.단점 : 재귀적인 기법으로 이해하기가 조금 어렵다. 그리고 이해와 구현이 어렵다.시간 복잡도 : O(nlogn) 퀵 정렬장점 : 평균적으로 빠른 속도를 내서 성능이 좋다.단점 : 안정적인 정렬을 보장하지 않는다.시간 복잡도 : 평균 시간 복잡도는 O(nlogn), 최악의 경우 시간 복잡도는 O(n²)메모이제이션계산 결과를 캐시에 저장하여 동일한 계산을 반복하지 않고 효율적으로 문제를 해결하는 동적 프로그래밍 기법이다.타뷸레이션문제를 작은 하위 문제로 나누어, 이들을 반복적으로 해결하고 결과를 표 형태로 저장하여 최종 결과를 도출하는 동적 프로그래밍 기법이다.[회고]칭찬하고 싶은 점강의를 들으면서 노션으로 강의 내용을 정리하는 점발자국도 작성하고 미션도 해결한 점모든 강의를 다 들은 점! 아쉬웠던 점역시 시간표대로 학습하지 않은 점.... 일하면서 공부하기 쉽지 않다... 그리고 강의를 들으면 들을 수록 어려워져서 100% 완벽하게 이해하고 넘어가질 못했다. 그래서 이 인프런 워밍업 클럽이 끝나도 강의를 반복해서 들어야 할 것 같다. 마지막 소감3주 동안 강의를 들으면서 공부를 했는데 회사 다니면서 하기 쉽지 않다.... 회사 다니시고 시간표 맞춰서 하시는 분들은 진짜 대단하신 것 같다. 나는 나태해서... 핫핫핫 그래도 시간표대로 강의를 듣지는 않았지만 주차 별로 끊어서 강의를 들으니 강의를 진행함에 있어서 좀 괜찮았다. 미션회고마지막 주차라 그런 건지 몰라도 점점 어려워지는 느낌이라 시간이 쫌 걸렸다. 그래도 끝까지 해결해서 다행이다! ㅎㅎCS 3주차 미션 링크https://www.inflearn.com/blogs/9005

발자국CS발자국인프런워밍업클럽

suover

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

입문자를 위한 Spring Boot with Kotlin - 나만의 포트폴리오 사이트 만들기강의와 함께한 인프런 워밍업 클럽 스터디 2기 - 백엔드 프로젝트 (Kotlin, Spring Boot) 3주차 발자국 입니다. 강의 수강이번 주에는 실습 중심으로 Spring Boot와 JPA를 사용한 컨트롤러 개발과 API 설계를 진행했습니다.API 테스트 코드를 작성해보고, Thymeleaf를 활용한 프론트 개발 작업도 수행했습니다.주요 학습 내용Spring MVC와 JPA를 활용한 컨트롤러 개발PresentationApiController 및 PresentationViewController에서 다양한 API 엔드포인트를 구현했습니다.JPA를 통해 데이터베이스와 상호작용하며 데이터를 처리하는 방법을 학습했습니다.Thymeleaf 프론트엔드 개발템플릿을 활용하여 데이터를 프론트엔드에 전달하고 뷰를 생성했습니다.커스터마이징을 통해 유지 보수가 용이한 뷰를 구성했습니다.컨트롤러 테스트 코드 작성API 테스트 코드를 작성하여 정확하게 동작하는지 검증했습니다.실제 API가 예상대로 동작하는지 검증하는 방법을 배웠습니다. 회고이번 주는 Spring Boot와 JPA를 활용한 컨트롤러 개발과 API 설계, 그리고 테스트 코드 작성까지 다양한 실습을 통해 실질적인 개발 경험을 쌓는 데 집중한 한 주였습니다. 특히, 데이터를 조회하고 처리하는 API를 설계하면서 RESTful 구조에 대해 깊이 고민했고, 테스트 코드 작성이 얼마나 중요한지 다시 한번 깨닫게 되었습니다. 실습을 통해 새로운 개념들을 익히고, 실제 프로젝트에 적용해보며 기술적 성장을 이뤘습니다.칭찬할 점테스트 코드를 작성하여 API의 주요 기능들을 효율적으로 검증했습니다.Thymeleaf 템플릿 커스터마이징을 통해 프론트엔드를 구성하고, 유지 보수가 용이하도록 최적화했습니다.아쉬웠던 점 및 보완할 점Thymeleaf 문법에 대한 이해가 부족하여 복잡한 작업에서 어려움이 있었습니다.테스트 코드를 더 간결하고 효율적으로 작성하기 위해 리팩토링 기법을 학습할 계획입니다.미션이번 주 미션은 게시글 조회 REST API를 개발하고, 이를 검증하기 위한 테스트 코드를 작성했습니다.API는 전체 게시글 조회와 특정 게시글 조회 두 가지 기능을 제공합니다. 미션 과정전체 게시글 조회 API 설계GET /api/posts 엔드포인트에서 전체 게시글을 조회하고, JSON 형식으로 데이터를 반환합니다.특정 게시글 조회 API 설계GET /api/posts/{postId}로 특정 게시글 ID를 기반으로 게시글을 조회하여 반환하는 기능을 구현했습니다.테스트 코드 작성 및 검증각 엔드포인트에 대해 단위 테스트를 작성하여 모든 기능이 예상대로 동작하는지 검증했습니다.특히 게시글이 없는 경우와 같은 예외 상황을 처리하는 테스트도 포함시켜 로직의 완성도를 높였습니다.  회고이번 미션을 통해 RESTful한 API 설계의 중요성을 다시 한번 느꼈습니다. 엔드포인트를 직관적이고 간결하게 설계하기 위해 많은 고민을 했으며, 테스트 코드를 작성하며 API 동작을 검증하는 과정에서 테스트의 중요성을 다시 한번 실감할 수 있었습니다.아쉬운 점 및 보완할 점테스트 코드의 가독성과 효율성을 높이기 위해 추가적인 리팩토링이 필요합니다.JPA의 복잡한 연관 관계 처리에서 어려움을 겪었으며, 이러한 부분을 더 명확하게 이해하기 위해 추가적인 학습이 필요합니다.이번 주의 학습과 미션을 통해 부족했던 부분들을 확인할 수 있었고, 앞으로도 지속적인 학습과 실습을 통해 문제를 개선해 나갈 계획입니다. 특히, JPA 연관 관계와 테스트 코드의 리팩토링에 집중하여 더 나은 품질의 코드를 작성하고, 실무에 적용할 수 있도록 역량을 키워가겠습니다.

백엔드인프런워밍업클럽스터디백엔드프로젝트발자국회고SpringBoot

양성빈

인프런 워밍업 스터디 클럽 2기 백엔드(클린코드, 테스트코드) 3주차 발자국

이 블로그 글은 박우빈님의 강의를 참조하여 작성한 글입니다.어느덧 벌써 워밍업 클럽이 막바지로 가고 있는 것 같다. 워밍업 클럽을 참여 전의 나보다 많이 성장했는가를 항상 발자국 쓸때 돌이켜 물어보는 것 같다. 과연 성장을 했을끼? 나는 당당히 성장을 하였다고 생각을 한다. 해당 스터디를 통해 나의 생활도 지식도 성장이 되었다 생각하며 해당에 대한 물음은 워밍업 클럽 수료 후에 다시 되물음을 해보겠다.이번 주차에서는 이제 Readable Code 강좌가 완강이 되고 Practical Testing 강좌를 시작하는 주차다. 이번주도 열심히 달려본 내역들을 작성해보겠다. 강의소개이 강좌는 테스트가 처음이거나 테스트 코드는 들어봤거나 작성하려고 시도를 해본 경험이 있는등 테스트가 궁금한 모든 분들을 위해 나온 강의이다. 나도 해당 테스트를 어떻게 하면 잘 작성할지가 궁금하여 이 강좌를 듣게 되고 해당 워밍업 클럽을 참여하게 된 이유이기도 하다.테스트를 작성하는 역량은 채용시장에서 주니어 개발자에게 기대하는 요소 중 하나다. 채용시 구현과제 등에서 테스트 작성여부, 테스트 코드 구현방식을 확인한다. 또한 소프트웨어의 품질을 보장하는 방법으로 그 중요성을 알고 있는지도 확인을 하기도 한다고 한다.이번 강좌에서는 다음과 같은 목표를 두고 학습을 진행한다고 한다.📚 목표1. 테스트 코드가 필요한 이유2. 좋은 테스트 코드란 무엇일까?3. 실제 실무에서 진행하는 방식 그대로 테스트를 작성해가면서 API를 설계하고 개발하는 방법4. 정답은 없지만 오답은 존재한다. 구체적인 이유에 근거한 상세한 테스트 작성 팁벌써부터 많은 기대를 품으며 다음 강의로 바로 가봐야 겠다. 어떻게 학습하면 좋을까?효과적인 학습을 하기 위해 가장 먼저 선행되어야 하는 것은 바로 무엇을 모르는지 아는 것이다. 무엇을 모르는 지 아는것은 찾아볼 수 있게 된다는 것이다.우리는 학습을 하면서 이 부분은 완벽히 아는 부분, 이 부분은 반만 아는 부분, 이 부분은 처음 들어보는 부분으로 구분된다. 그래서 강좌에서 함계 학습한 키워드와 추가 학습을 위한 키워드를 분리하여 키워드 기반으로 정리를 해주신다고 하니 많은 기대를 가지며 다음 강의부터 본격적으로 달려 볼 예정이다. 테스트는 왜 필요할까?기술 학습에 있어서 '왜?'가 중요하다. 테스트하면 생각나는게 무엇일까? 나는 처음 테스트코드를 볼때 굳이 해야하나? 개발시간만 더 늘릴뿐일텐데라는 생각을 하였다.그런데 만약 테스트코드가 없이 실제 인간이 수동으로 테스트를 하면 매우 큰 문제들을 야기할 수 있다. 인간은 실수의 동물이기 때문이다. 또한 만약 기능을 개발할때 기존 기능을 건들게 된다면 기존 기능도 다시 테스트를 하는 시간낭비가 발생한다. ✅ 테스트 작성을 안하면?1. 커버할 수 없는 영역 발생2. 경험과 감에 의존3. 늦은 피드백4. 유지보수 어려움5. 소프트웨어에 대한 신뢰가 떨어딘다. 테스트 코드를 작성하지 않으면?변화가 생기는 매 순간마다 발생할 수 있는 모든 case를 고려해야한다.변화가 생기는 매 순간마다 모든 팀원이 동일한 고민을 해야한다.빠르게 변화하는 소프트웨어의 안정성을 보장할 수 없다.테스트코드가 병목이 된다면?프로덕션 코드의 안정성을 제공하기 힘들어진다.테스트 코드 자체가 유지보수하기 어려운 새로운 짐이 된다.잘못된 검증이 이루어질 가능성이 생긴다.올바른 테스트 코드자동화 테스트로 비교적 빠른 시간 안에 버그를 발견할 수 있고 수동 테스트에 드는 비용을 크게 절약할 수 있다.소프트웨어의 빠른 변화를 지원한다.팀원들의 집단 지성을 팀 차원의 이익으로 승격시킨다.가까이 보면 느리지만 멀리보면 빠르다.샘플 프로젝트 소개 & 개발 환경 안내해당 테스트 섹션에서는 카페 키오스크 시스템을 만들면서 테스트 학습을 할 예정이다.🛠 개발환경- IntelliJ Ultimate- Vim(Plugin) 프로젝트 세팅인텔리제이를 활용하여 스프링부트 프로젝트를 생성하고 build.gradle의 의존성 정리를 하였다. 수동테스트 VS. 자동화된 테스트요구사항주문목록에 음료 추가/삭제 기능주문목록에 전체 지우기주문목록 총 금액 계산하기주문 생성하기 해당 부분을 토대로 콘솔기반 비즈니스 로직을 작성하였고 테스트 강의이니 해당 로직을 테스트 하기 위해 이 중 음료 추가에 대한 로직을 아래와 같이 작성했다.@Test void add() {     CafeKiosk cafeKiosk = new CafeKiosk();     cafeKiosk.add(new Americano());     System.out.println(">>> 담긴 음료 수: " + cafeKiosk.getBeverages().size());     System.out.println(">>> 담긴 음료: " + cafeKiosk.getBeverages().get(0).getName()); }위의 코드를 봤을 때 이렇게 테스트코드를 짜면 안된다고 직감을 했을 것이다. 왜냐하면 일단 최종단계에서 사람이 개입하고 어떤게 맞고 어떤게 틀리는지 모른다는 것이다. 또한 이 테스트는 100% 성공하는 케이스이기 때문에 뭔가 테스트라고 하기에 모호한것 같다. JUnit5로 테스트하기단위테스트작은 코드 단위를 독립적으로 검증하는 테스트검증속도가 빠르고 안정적이다.Junit5단위 테스트를 위한 테스트 프레임워크 AssertJ테스트 코드 작성을 원활하게 돕는 테스트 라이브러리풍부한 API, 메서드 체이닝 지원 해당 지식을 기반으로 우리가 이전 시간에 작성한 아메리카노부분과 카페머신의 대한 단위 테스트를 AssertJ를 이용하여 작성해보는 시간을 가졌다. 테스트 케이스 세분화하기스스로에게 질문해보자. 암묵적이거나 아직 드러나지 않은 요구사항이 있는지를 확인해보자. 그리고 해피케이스와 예외케이스를 둘다 생각하며 항상 경계값 테스트를 해보자.경계 값은 범위(이상, 이하, 초과, 미만), 구간, 날짜등을 일컫는다.그래서 우리는 음료에 여러잔을 담는 기능을 개발하고 해당 부분의 해피케이스에 관한 테스트를 작성했다. 또한 예외 케이스를 생각해 로직을 작성하고 해당 예외케이스에 대한 로직을 작성하게 되었다. 테스트하기 어려운 영역을 분리하기테스트하기 어려운 영역은 다음과 같다.관측할 때마다 다른 값에 의존하는 코드현재 날짜/시간, 랜덤 값, 전역변수/함수, 사용자 입력 등외부세계에 영향을 주는 코드표준출력, 메세지 발송, 데이터베이스 기록 그래서 우리는 실습으로 주문을 생성할때 가게 영업시간이 아닐시, 주문을 못하게 하는 상황의 로직을 작성했고 테스트코드 작성 시 문제가 생겼다. 내가 현재 새벽에 테스트코드를 돌렸고 영업시간 전이기에 테스트코드가 실패한것이다. 결국 이 부분은 날짜를 파라미터로 받게 변경하여 해결하였다.📚 순수함수- 같은 입력에는 같은 결과- 외부세상과 단절된 형태- 테스트하기 쉬운 코드 TDD: Test Driven Development프로덕션 코드보다 테스트 코드를 먼저 작성하여 테스트가 구현과정을 주도하도록 하는 방법론Red: 실패하는 테스트 작성Green: 테스트 통과하기 위한 최소한의 코딩Refactor: 구현코드 개서느 테스트 통과 유지피드백TDD는 빠르게 피드백을 자동으로 받을 수 있다.선 기능 후 테스트 작성테스트 자체의 누락 가능성특정 테스트 케이스(해피 케이스)만 검증할 가능성이 크다.잘못된 구현을 다소 늦게 발견할 가능성이 있다.선 테스트 후 기능 작성복잡도가 낮은 테스트 가능한 코드로 구현할 수 있게 된다.유연하며 유지보수가 쉬운쉽게 발견하기 어려운 엣지 케이스를 놓치지 않게 해준다.구현에 대한 빠른 피드백 가능과감한 리팩토링이 가능클라이언트 관점에서의 피드백을 주는 Test Driven 테스트는 []다.테스트는 무엇일까? 테스트는 문서라고 볼 수 있다.프로덕션 기능을 설명하는 테스트 코드 문서다양한 테스트 케이스를 통해 프로덕션 코드를 이해하는 시각과 관점을 보완어느 한 사람이 과거에 경험했던 고민의 결과물을 팀 차원으로 승격시켜서 모두의 자산으로 공유할 수 있다. DisplayName을 섬세하게@DisplayName을 사용하여 테스트 명을 구체화할때 명사의 나열보단 문장으로 작성하는 것이 좋다. 또한 테스트 행위에 대한 결과를 기술하는데 도메인 용어를 사용하여 매서드 자체의 관점보다 도메인 정책 관점으로 한층 추상화된 내용을 담는것이 좋다. 마지막으로 테스트의 현상을 중점으로 기술하지 말자. 예를 들어 ~실패라기 보단 도메인의 내용을 담는것이 좋을 것 같다. BDD(Behavior Driven Development) 스타일로 작성하기TDD에서 파생된 개발방법함수단위의 테스트에 집중하기보다 시나리오에 기반한 테스트 케이스(TC) 자체에 집중하여 테스트개발자가 아닌 사람이 봐도 이해할 수 있을 정도의 추상화 수준(레벨)을 권장 Given / When / ThenGiven: 시나리오 진행에 필요한 모든 준비 과정(객체, 값, 상태 등)When: 시나리오 행동 진행Then: 시나리오 진행에 대한 결과 명시 및 검증 어떤환경에서(Given) 어떤 행동을 진행했을 때(When) 어떤 상태 변화가 일어난다.(Then)라는것을 토대로 @DisplayName을 상세히 적을 수 있다. 미션이번 미션은 저번에 Readable Code에서 진행했던 마지막 과제 프로젝트인 '지뢰찾가', '스터디카페'중에 1개의 프로젝트를 가지고 테스트 코드를 작성해보는 시간을 가졌다. 조건은 BDD스타일로 3개이상의 클래스 총 7개 이상 테스트를 작성하는 것이었지만 나는 한층 공부한다는 마음으로 테스트 커버리지 툴인 jacoco를 가지고 스터디 카페부터 진행을 하였다. 그 결과 테스트 커버리지 98%라는 결과를 가지게 되었다. 그리고 조금 더 욕심이 나서 지뢰찾기도 일부 클래스를 진행하였다. 이번 미션을 해보면서 어려웠고 힘들었지만 테스트 작성에 많이 익숙해진 경험을 가지게 되었다. 깃허브 주소 레이어드 아키텍쳐(Layered Architecture)와 테스트레이어드 아키텍쳐에서는 아래와 같이 구성되어 있다.- Persentation Layer- Business Layer- Persistence Layer이렇게 레이어를 나눈 이유는 관심사의 분리때문일 것이다. 책임을 나눔으로서 유지보수성을 쉽게 가져가기 위함이다.🙋🏻 테스트 하기 어려워보여요!그렇게 보일 수는 있겠지만 앞선것과 기조는 비슷하다. 즉, 테스트하기 어려운 걸 분리하여 테스트하고자 하는 영역을 집중하며 명시적이고 이해할 수 있는 문서형태로 테스트 작성하는 것은 어떤 아키텍쳐든 동일하다.A와 B라는 모듈이 있다고 하자. 이 두 모듈을 더했을 때 뭐가 나올까? AB? BA? C? 누구도 예측하기 힘들다. 그래서 우리는 통합 테스트의 필요성이 느껴질 것이다.통합 테스트여러 모듈이 협력하는 기능을 통합적으로 검증하는 테스트일반적으로 작은 범위의 단위테스트만으로는 기능 전체의 신뢰성 보장X풍부한 단위 테스트 & 큰 기능 단위를 검증하는 통합 테스트의 조화가 필요. Spring / JPA 훑어보기 & 기본 엔티티 설계Spring스프링을 애기하면 먼저 라이브러리와 프레임워크의 차이를 묻는다. 라이브러리 같은 경우는 내 코드가 주최가 된다. 즉, 필요한 기능이 있다면 외부에 끌어와서 사용을 하는데 이게 라이브러리다. 반면 프레임워크는 이미 프레임(동작환경)이 있고 내 코드가 주최가 아니고 내 코드는 수동적으로 이 안에 들어가서 역할을 하는데 이게 프레임워크다.스프링을 애기하면 나오는 주요 3가지가 존재한다. IoC, DI, AOP다.- IoC(Inversion of Control): 객체의 생성과 의존성 관리를 프레임워크에 위임하는 개념.- DI(Dependency Injection): 의존성 주입을 통해 객체 간 결합도를 낮추고 확장성과 테스트 용이성을 향상시킴.- AOP(Aspect-Oriented Programming): 횡단 관심사(공통 기능)를 분리하여 코드 중복을 줄이고 모듈성을 개선.ORM객체지향과 RDB 페러다임이 다름.이전에는 개발자가 객체의 데이터를 한땀한땀 매핑하여 DB에 저장 및 조회ORM을 사용함으로써 개발자는 단순 작업을 줄이고 비즈니스 로직에 집중. JPAJava진영의 ORM 기술 표준인터페이스이고 여러 구현체가 있지만 보통 Hibernate를 많이 사용반복적인 CRUD SQL을 생성 및 실행해주고 여러 부가 기능들을 제공편리하지만 쿼리를 직접 작성하지 않기 때문에 어떤식으로 쿼리가 만들어지고 실행되는지 명확하게 이해하고 있어야 함Spring 진영에서는 JPA를 한번 더 추상화한 Spring Data JPA제공QueryDSL과 조합하여 많이 사용 Persistence Layer 테스트요구사항이 다음과 같다고 하자.키오스크 주문을 위한 상품 후보 리스트 조회하기상품의 판매 상태: 판매중, 판매보류, 판매금지판매중, 판매보류인 상태의 상품을 화면에 보여준다.id, 상품번호, 상품타입, 판매상태, 상품이름, 가격 이 요구사항을 바탕으로 우리는 엔티티설계부터해서 컨트롤러까지 즉, Presentation Layer, Business Layer, Persistence Layer까지 전반적으로 한 사이클을 돌면서 코드를 작성해보고 확인까지 진행해보았다. 그럼 이제 repository부분부터 테스트를 해보자.우리는 given-when-then 패턴으로 테스트 코드를 작성했다. 여기서 살펴볼 것은 @SpringBootTest와 @DataJpaTest이다. 이 둘의 비슷하지만 차이점을 살펴보면 @SpringBootTest는 모든 부분의 의존성들을 주입시켜주지만 @DataJpaTest는 JPA관련된 부분만 주입을 시켜준다. 따라서 @DataJpaTest가 더 가볍다. 하지만 우빈님께서는 @SpringBootTest를 선호하신다고 하신다. 그 이유에 대해서는 추후에 말씀주신다고 하셨다.그럼 Persistence Layer 역할에 대해 정리하면 아래와 같다.Data Access 역할비즈니스 가공로직이 포함되어서는 안된다. Data에 대한 CRUD에만 집중한 레이어여야 한다.ex) QueryDSL이나 별도 DAO를 사용하면서 비즈니스 로직이 침투할 가능성이 있을 수 있으니 이 점을 생각하면서 작성해야 할 것 같다. Business Layer 테스트비즈니스 레이어에 대한 역할을 살펴보면 아래와 같다.비즈니스 로직을 구현하는 역할persistence layer와의 상호작용(Data를 읽고 쓰는 행위)을 통해 비즈니스 로직을 전개시킨다.트랜잭션을 보장해야한다. 그래서 우리는 새로운 요구사항을 통해 해당 비즈니스 레이어에 대한 테스트 코드를 작성해보는 시간을 가졌다. 새로운 요구사항은 아래와 같다.요구사항(1)상품번호 리스트를 받아 주문 생성하기주문은 주문상태 주문등록시간을 가진다.주문의 총 금액을 계산한다. 위의 요구사항으로 우리는 주문 엔티티를 설계하고 연관관계를 기존에 만든 상품 엔티티와 다대다 관계를 맺기 위해 중간 엔티티를 설계하고 각각으로 연관관계를 맺어두었다. 그리고 주문생성 로직과 테스트코드를 작성하는 시간을 가졌다.다음으로 우리가 작성한 테스트들을 동시에 돌려보았다. 하지만 실패되는 테스트를 보게되었다. 우리가 작성한 비즈니스 레이어 테스트가 전부 성공하는게 아니라 일부 실패가 되는 경우가 있다. 하지만 이 전에 Persistence Layer에 작성한 테스트를 동시 실행해보면 그것은 괜찮았다. 차이는 @SpringBootTest와 @DataJpaTest 두 어노테이션 차이였다. 두 어노테이션을 타고 들어가서 확인하면 @Transactional 어노테이션 유무 차이였다. 그래서 실패되는 비즈니스 레이어에 트랜잭션 어노테이션을 붙여주면 될 것 같아 보였지만 우빈님께서는 tearDown 메서드를 만드셔서 데이터를 클리닝하는 작업을 해주셨다. 그 이유는 추후에 말씀주신다고 하셨다. 요구사항(2)주문 생성 시 재고 확인 및 개수 차감 후 생성하기재고는 상품번호를 가진다.재고와 관련 있는 상품타입은 병음료, 베이커리다. 새로운 요구사항으로 재고 개념이 도입되었다. 그래서 해당 엔티티를 설계후 개수 차감 로직을 작성하였다. 여기서 위에서 언급한 수동으로 tearDown 메서드로 삭제를 하나하나 해주냐 아니면 @Transactional 어노테이션을 붙여주냐였다. 처음에 우리는 로직을 작성하고 해당 로직을 테스트할때 @Transactional어노테이션을 붙이지 않고 tearDown 메서드를 만들고 실행하였고 결과는 실패하였다. 정상적으로 재고가 감소가 안 된 것이다. 그래서 해당 로그와 쿼리를 보니 update 쿼리가 안 나간것이다. 원래 @Transactional 어노테이션을 붙이면 커밋종료시점에 더티체킹으로 update 쿼리가 발생한다. 하지만 지금은 우리가 수동으로 감소하는 전략을 하였기에 더티체킹 기능이 활성이 안 된 것이다. 🙋🏻 그러면 왜 insert쿼리는 잘 나간거에요?jpa repository를 타고 들어가보면 crud repository를 확인할 수 있다. 해당 구현체를 보면 save 메서드에 @Transactional 어노테이션이 잘 붙어져 있다. 이것은 delete도 마찬가지다.그래서 우리는 추후 살펴볼 것들이 있어 tearDown 메서드는 두고 @Transactional을 product 코드에 적용하기로 했다. 그리로 우빈님께서 이런 경우를 대비해 테스트에는 @Transactional을 붙이고 실질적으로 본 코드에는 안 붙이고 release하는 경우도 있으니 한번 생각하고 써야한다고 말씀을 주셨다.추가적으로 재고감소 로직은 동시성 이슈가 날 수 있는 대표적엔 케이스다. 지금은 키오스크가 1대밖에 없다 가정했지만 2대 이상이라면 동시성 이슈가 터질 것이다. 그래서 optimistic lock / pessimistic lock등을 고민해서 해결을 해야한다. 이 부분도 나중에 한번 더 스스로 공부해봐야겠다.

백엔드인프런워밍업스터디클럽2기백엔드클린코드테스트코드발자국

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

운영체제 1주차에 배웠던 것들을 바탕으로 깊숙하게 학습하는 것 같다.cpu 스케줄링에 대해 학습할 때 FIFO의 개념과 단점으로부터 점점 발전되는 SJF, RR, MLFQ 알고리즘이 등장한다.평소에 아무런 생각없이 사용했던 컴퓨터에 이러한 알고리즘이 적용됨을 알아간다는 것이 흥미로웠다.하지만 점점 기초지식이 아니기 때문에 5분짜리 강의라고 해도 두번 듣고 노션에 캡쳐하며 정리까지 하다보니 강의 시간의 3배는 걸리는 것 같다.그래도 그림과 함께 설명해주시니 이해가 잘 된다는 장점이 있기 때문에 놓치지 않고 꼼꼼히 필기하며 들어야겠다는 생각을 가진다.위 강의를 바탕으로 후에 정보처리기사에 도전해봐야겠다  자료구조와 알고리즘 작년에 프로그래밍 언어를 처음 배우면서 재귀함수가 정말 어려웠다.하지만 강사님의 꿀팁을 바탕으로 생각하다보니 이전보다 더 빠르게 코드를 이해하고 풀 수 있었다.정렬도 작년에 배우고 써먹지 않다보니 가물가물했었는데 강의를 통해 한번 더 리마인드 할 수 있었다.알고리즘을 들으면서 느끼는 건 꾸준함이 정말 중요하다 생각이 든다.작년에는 알고 있었지만 시간이 지나며 적용하지 않다보니 다 잊어버려 실무에 적용하지 못한다는 점이다. 강의를 들으면서 C#에도 적용해보고 잊지 않도록 리마인드하는 과정을 거쳐야겠다

발자국

karl

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

지난 일주일 동안의 강의 내용 중 일부를 발췌했습니다. 자세한 내용은 개인 노션 페이지에 타이핑했습니다.자료구조와 알고리즘재귀(Recursion): 어떠한 것을 정의할 때 자기 자신을 참조하는 것재귀함수: 함수 정의 내에 같은 이름의 함수가 올 때 이를 재귀함수라 한다.반드시 탈출 조건(기저조건)이 있어야 한다.콜스택: 함수가 호출되면서 올라가는 메모리 영역으로, 스택이라고도 부른다. 운영체제운영체제가 중간에서 CPU를 할당해주는 것을 CPU 스케줄링이라 부른다.CPU 스케줄링은 공평함과 성능 문제 때문에 프로세서들에게 일정시간, 즉 타임슬라이스만큼 CPU를 할당하기로 했다.이 때문에 공유된 자원에서 문제가 발생하고 이를 동기화 문제라고 부르기로 했다.이를 해결하기 위한 방법인 세마포어와 모니터를 배웠다. 동기화 문제를 해결하기 위해 공유된 자원을 한 프로세스가 점유하게 만들었으나 교착상태(데드락)라는 것이 발생했습니다.교착상태가 발생하는 원인과 해결 방법을 알아봤다.회고잘한 점강의 계획 일정에 따라 밀리지 않고 들었다. 처음 알게되었던 내용을 알 수 있어서 좋았다.아쉬운 점나름대로 정리한 강의 내용이 다시 복습할 때는 글로만 적어서 이해하는데 약간 시간이 걸렸다. 강의 캡처 기능을 이용하여 그림으로 설명해 주는 것도 함께 볼 수 있도록 정리해야겠다.목표꾸준히 강의 듣고 학습하기 이전에 배웠던 내용도 꾸준히 복습하기정리할 때 캡처 기능도 활용하기미션(https://inf.run/W9XSk)미션을 해결한 과정지난 한 주 동안 배운 내용을 복기하여 해결했다. 회고노션에 정리한 내용으로 풀었는데 정리한 내용을 다시 볼 때 이해하기 어려웠던 내용이 있었다. 다음부터는 강의를 들을 때 좀 더 자세하게 정리해야겠다.

발자국워밍업클럽CS

[인프런 워밍업 클럽 CS 2기] CS 2주차 미션

운영체제FIFO 스케줄링의 장단점이 뭔가요?장점단순하고 직관적단점한 프로세스가 완전히 끝나야 다음 프로세스가 시작할 수 있음실행시간이 짧고 늦게 도착한 프로세스가 실행시간이 길고 빨리 도착한 프로세스의 작업을 기다려야함I/O작업이 있을 때 CPU는 I/O작업이 끝날 때까지 쉬고 있기 때문에 CPU 사용률이 떨어지게 됨 2. SJF를 사용하기 어려운 이유가 뭔가요?어떤 프로세스가 얼마나 실행될지 예측이 어렵다.Burst Time이 긴 프로세스는 아주 오랫동안 실행되지 않을 수도 있다(Burst Time이 짧은 프로세스가 먼저 실행되기 때문) 3. RR 스케줄링에서 타임 슬라이스가 아주 작으면 어떤 문제가 발생할까요?컨텍스트 스위칭이 자주 일어나고, 타임 슬라이스에서 처리되는 처리 량보다 컨텍스트 스위칭을 처리하는 양이 훨씬 커져서 오버헤드가 너무 커짐 4.운영체제가 MLFQ에서 CPU Bound Process와 I/O Bound Process를 어떻게 구분할까요?CPU를 사용하는 프로세스가 실행하다가 스스로 CPU를 반납하면 CPU사용이 적은 것이니 I/O Bound Process일 확률이 높다고 볼 수 있음.CPU를 사용하는 프로세스가 타임 슬라이스 크기를 오버해서 CPU 스케줄러에 의해 강제로 CPU를 뺏기는 상황이면 CPU 사용이 많은 것이니 CPU Bound Process일 확률이 높다고 볼 수있음. 공유자원이란무엇인가요? 프로세스 간 통신을 할 때 공동으로 이용하는 변수나 파일들 교착상태에 빠질 수 있는 조건은 어떤 것들을 충족해야할까요?임계영역엔 동시에 하나의 프로세스만 접근한다.여러 요청에도 하나의 프로세스의 접근만 허용한다.임계구역에 들어간 프로세스는 빠르게 나와야한다.자료구조와 알고리즘재귀함수에서 기저조건을 만들지 않거나 잘못 설정했을 때 어떤 문제가 발생할 수 있나요? 함수는 종료 조건에 도달하지 못하고 계속해서 자기 자신을 호출하게 된다.(무한루프)0부터 입력 n까지 홀수의 합을 더하는 재귀 함수를 만들어보세요.우선 무한루프가 발생하지 않도록 n이 0에 도달하면 0을 반환하도록 조건문 추가한다.홀수의 합을 더하는 함수이니 n을 숫자 2로 나누었을 때 나머지가 0이 나오지 않도록 조건문을 작성하고 만약에 홀수라면 n을 더하고 n-1을 해서 재귀함수를 호출하고, 짝수일 경우 n에 더하지 않고 n-1로 재귀호출한다.function sumOdd(n){ if (n <= 0) { return 0; } if (n % 2 !== 0){ return n + sumOdd(n - 1) }else{ return sum_odd(n - 1) } } console.log(sumOdd(10)) // 25

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

하얀종이개발자

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

운영체제 2주차 학습 요약 CPU 스케쥴링 알고리즘SJF (Shortest Job First)짧은 작업 시간을 가진 프로세스에 먼저 CPU를 할당하는 방식버스트 타임이 긴 프로세스는 계속 실행되지 않을 수 있고, 어떤 프로세스가 얼마나 실행될지 예측이 어려움RR (Round Robin)시간을 균등하게 분배하여 각 프로세스에 순차적으로 CPU를 할당하는 방식컨텍스트 스위칭으로 인해 처리량이 늘어남MLFQ (Multi Level Feedback Queue)프로세스의 우선순위를 동적으로 조절하여 CPU를 효율적으로 할당하는 방식오늘날 운영체제에서 가장 일반적으로 사용됨프로세스 동기화여러 프로세스가 공유 자원에 동시에 접근할 때 순서를 정하여 데이터의 일관성을 유지하는 것프로세스간 통신의 종류한 컴퓨터 내에서 프로세스간 통신하는 방법 : 파일 or 파이프를 이용한 프로세스 내에서 쓰레드간 통신하는 방법 : 데이터 or 힙영역을 이용네트워크를 이용하여 통신하는 방법 : 소켓통신, RPC공유자원과 임계영역공유자원은 프로세스 혹은 스레드간 통신할 때 공동으로 이용하는 변수나 파일 같은 것들공유자원은 여러 프로세스가 공유하고 있기 때문에 각 프로세스의 접근 순서에따라 결과가 달라질 수 있음임계영역은 프로세스 혹은 스레드가 동시에 사용하면 안되는 영역 (접근 순서등의 이유로 결과가 달라지는 영역)경쟁조건은 공유자원을 서로 사용하기 위해 경쟁하는 것임계구역 문제를 해결하기 위한 조건상호배체임계영역엔 하나의 프로세스만 접근해야 함한정대기기다리는 프로세스는 언제가는 임계영역에 접근 할 수 있어야함진행의 융통성한 프로세스가 다른 프로세스의 일을 방해해서는 안됨세마포어 & 모니터뮤텍스는 공유자원을 lock()과 unlock()을 이용하여 프로세스의 접근을 제어하는 메커니즘여러 프로세스의 접근을 관리할 수 있으면 세마포어 (뮤텍스는 동기화 대상이 오직 하나임)세마포어는 잘못된 사용으로 임계영역을 보호받지 못할 수 있음 (세마포어를 사용하지 않고 임계구역에 들어간 경우)이를 해결한 방식이 모니터모니터는 공유자원을 숨기고 접근에 대한 인터페이스만 제공하여 공유자원에 안전하게 접근하게 하는 메커니즘운영체제가 처리하는게 아니라 프로그래밍 언어차원에서 지원하는 방법임교착상태 (데드락)두개 이상의 프로세스들이 서로가 가진 자원을 기다리다 아무도 작업을 진행하지 못하는 상태데드락 필요조건상호배제비선점점유대기원형대기은행원 알고리즘총 자원의 양과 현재 할당한 자원의 양을 기준으로 안정 또는 불안정 상태로 나누고, 교착상태가 발생하지 않는 수준이 되도록 자원을 할당하는 알고리즘검출 & 회복교착상태는 어떻게 검출하지?가벼운 교착상태 검출 (타이머)일정시간마다 작업을 조작하고 교착상태가 발생하면 체크포인트로 롤백무거운 교착상태 검출 (순환구조)교착상태를 일으킨 프로세스를 강제 종료시키고 다시 실행 시킬때 체크포인트로 롤백오버헤드가 발생하지만 억울하게 종료되는 프로세스는 발생하지않음메모리레지스터 (32bit 또는 64bit)캐시CPU와 메인메모리 속도 차이 때문에 미리 데이터를 저장하는 임시공간메인메모리 (RAM)프로세스를 로드, 휘발성보조기억장치 (SSD, HDD)디스크 저장, 비휘발성물리 주소 - 물리적인 메모리 주소논리 주소 - 사용자 관점에서 본 상대적 주소, 사용자는 논리 주소를 통해 물리 주소로 접근메모리 할당방식가변분할방식메모리에 연속해서 프로세스를 할당단점으로 외부 단편화가 발생할 수 있음고정분할방식메모리를 정해진 크기로 나누어 프로세스를 할당단점으로 내부 단편화가 발생할 수 있음버디시스템메모리를 2의 제곱 수로 분할하여 할당하는 방식가변분할방식과 고정분할방식을 합친 방법 알고리즘 & 자료구조 2주차 학습 요약 재귀 & 재귀적으로 생각하기재귀는 자기 자신을 다시 호출하는 함수를 말함어떤 문제를 해결하기 위해 문제의 부분 문제를 같은방식으로 해결하는 과정하향식 풀이에서 활용됨 (하위 문제를 기반으로 해결)하노이탑 구현하기세개의 기둥과 서로 다른 크기의 원반들이 있을때 가장 큰 원반이 아래에 있고 위로 갈수록 작은 원반으로 이루어져있음하나의 기둥에 있는 원반들을 다른 기둥으로 옮겨야 함기둥 A에 있는 원반을 기둥 C로 옮기기 (하향식 접근으로 풀이)  처음 시작 그림으로 쉽게 배우는 자료구조와 알고리즘 (기본편) 캡쳐  모두 옮김그림으로 쉽게 배우는 자료구조와 알고리즘 (기본편) 캡쳐자바코드public class HanoiTop { void hanoi(int count, String from, String to, String temp){ if(count == 0) return; hanoi(count - 1, from, temp, to); System.out.printf("원반 %d를 %s에서 %s로 이동\n", count, from, to); hanoi(count - 1, temp, to, from); } public static void main(String[] args) { HanoiTop hanoiTop = new HanoiTop(); hanoiTop.hanoi(3, "A", "C", "B"); } } 버블정렬앞에 있는 숫자와 옆에 있는 숫자를 비교해서 자리를 바꾸는 알고리즘자바코드public class BubbleSort { void bubbleSort(int[] arr) { // 사이클 - 자리교체는 (배열의 갯수 - 1) 번 수행함 for (int i = 0; i < arr.length - 1; i++) { // 횟수 - 정렬이 된 원소의 이전 원소보다 하나 이전의 원소까지 순회 // 시작점이 (arr.length-i-1)번 임 for (int j = 0; j < (arr.length - i - 1); j++) { // 앞의 데이터가 뒤에 데이터보다 더 크다면? if (arr[j] > arr[j + 1]) { // 데이터 자리변경 int temp = arr[j]; arr[j] = arr[j + 1]; arr[j + 1] = temp; } } } } public static void main(String[] args) { int[] arr = {2, 3, 1, 4}; // 정렬 전 - arr = [2, 3, 1, 4] System.out.println("정렬 전 - arr = " + Arrays.toString(arr)); BubbleSort bubbleSort = new BubbleSort(); bubbleSort.bubbleSort(arr); // 정렬 후 - arr = [1, 2, 3, 4] System.out.println("정렬 후 - arr = " + Arrays.toString(arr)); } } 선택정렬정렬되지 않은 배열의 첫번째 원소를 시작으로 마지막 원소까지 탐색하여 가장 작은 값을 정렬되지 않은 첫번째 배열로 가져오는 알고리즘자바코드public class SelectionSort { void selectionSort(int[] arr) { // 1 사이클의 순회는(배열의 갯수 - 1) 번 수행됨 for (int i = 0; i < arr.length - 1; i++) { int minValueIndex = i; // 가장 작은 값을 탐색하기위해 순회 for (int j = i + 1; j < arr.length; j++) { if (arr[j] < arr[minValueIndex]) { // 작은값 저장 minValueIndex = j; } } // 자리 변경 int temp = arr[i]; arr[i] = arr[minValueIndex]; arr[minValueIndex] = temp; } } public static void main(String[] args) { int[] arr = {4, 2, 1, 3}; // 정렬 전 - arr = [4, 2, 1, 3] System.out.println("정렬 전 - arr = " + Arrays.toString(arr)); SelectionSort selectionSort = new SelectionSort(); selectionSort.selectionSort(arr); // 정렬 후 - arr = [1, 2, 3, 4] System.out.println("정렬 후 - arr = " + Arrays.toString(arr)); } }  회고2주차 스터디를 진행하면서 정해진 진도도 학습하면서, 스터디안에 발표스터디에 참여해서 복습도 하는 과정이었어요. 적어보였던 운영체제의 내용이 생각보다 많아서 정리하는데 은근 시간이 오래 걸리더라구요. 그래도 스터디안의 발표 스터디에 참여하여 정리내용을 발표하고 다른 스터디원들의 발표내용도 듣고 헷갈리는 부분도 토론해서 도움이 많이 되었던 한 주 였던것 같습니다.스터디 발표 정리 자료 캡쳐그리고 이번 알고리즘 강의에서는 항상 어렵게 느꼈던 재귀에 대해 조금은 이해한게 너무 좋았습니다.어떤 문제를 해결하기 위해 문제의 부분 문제를 같은 방식으로 분해하여 해결한다.!! 재귀함수 내에서 자기 자신을 호출할 때 이 함수가 벌써 구현이 되어있다고 가정하고 재귀함수를 구현한다.!! 많이 연습해봐야겠지만 그래도 재귀적으로 생각하는 방법을 느낄 수 있었던 것 같습니다.마지막 1주가 남았는데, 끝까지 열심히 해서 끝까지 완주해야겠네요. 화이팅

백엔드인프런워밍업클럽2기CS전공지식2주차발자국감자

suover

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

입문자를 위한 Spring Boot with Kotlin - 나만의 포트폴리오 사이트 만들기강의와 함께한 인프런 워밍업 클럽 스터디 2기 - 백엔드 프로젝트 (Kotlin, Spring Boot) 2주차 발자국 입니다. 강의 수강이번 주에는 JPA를 활용해 데이터를 초기화하고, 기본적인 CRUD 작업을 진행하면서 프로젝트의 기초적인 부분을 다졌습니다. 강의를 통해 실습을 진행하며, 코드 작성과 실행 과정을 통해 JPA와 Spring Boot에 익숙해질 수 있었습니다. 특히, 강의를 통해 코드를 작성하는 과정에서 JPA의 기본 사용법을 익히는 데 집중했습니다.주요 학습 내용Spring Data JPA의 기본 CRUD 기능 실습엔티티 간의 기본적인 연관 관계 설정JPA와 데이터베이스 초기화를 통한 실습 환경 설정테스트 코드 작성과 JUnit을 활용한 기본적인 테스트 환경 설정 회고강의와 함께 실습 프로젝트를 진행하면서 JPA와 Spring Boot의 기능을 익혔습니다. 이론 학습보다는 코드 작성에 집중하여, Spring Boot와 JPA의 실질적인 사용법을 몸에 익히는 데 중점을 두었습니다. 실습을 통해 프로젝트 구조와 JPA의 기초 개념을 이해하는 데 도움을 받았습니다.칭찬할 점매일 강의를 듣고 실습 프로젝트를 꾸준히 진행한 점새로운 기술을 익히며 이를 프로젝트에 바로 적용해 본 점실습을 통해 테스트 코드 작성법을 익혀본 점 아쉬웠던 점 및 보완할 점강의를 따라가는 데 집중하다 보니 개념적인 이해가 부족한 부분이 있습니다.실습을 통해 얻은 질문이나 궁금증을 정리해두고, 추가적인 학습을 통해 보완할 계획입니다.미션이번 주 미션은 REST API 설계하기로, 사용자가 게시글과 댓글을 작성하고, 좋아요를 추가하거나 삭제할 수 있는 RESTful API를 설계하는 것이 목표였습니다. 이를 통해 사용자 관리, 게시글과 댓글 관리, 좋아요 기능을 포함한 API를 구축했습니다. 미션 과정API 설계사용자 API: 회원가입, 로그인, 로그아웃, 사용자 정보 조회 및 수정, 비활성화 기능을 설계했습니다.게시글 API: 게시글 작성 및 수정, 삭제 기능과 더불어 게시글 목록 조회 및 특정 게시글 조회를 위한 엔드포인트를 설계했습니다. 또한, 좋아요 추가 및 취소 기능도 포함하여 사용자의 상호작용을 풍부하게 했습니다.댓글 및 답글 API: 댓글 작성, 수정, 삭제 기능과 게시글별 댓글 목록을 조회할 수 있는 기능을 설계했습니다. 댓글에 좋아요를 추가하거나 삭제할 수 있는 기능도 포함하여 사용자가 게시물과 댓글에 대해 보다 활발히 상호작용할 수 있도록 했습니다.  구체적인 API 엔드포인트 및 메서드사용자 API: /api/users 및 /api/users/{userId}를 통해 사용자를 관리할 수 있도록 하였고, 로그인, 로그아웃, 비활성화 기능을 위한 별도의 엔드포인트를 설정했습니다.게시글 API: /api/posts 및 /api/posts/{postId}를 통해 게시글 CRUD와 좋아요 기능을 관리할 수 있도록 설계했습니다.댓글 및 답글 API: /api/posts/{postId}/comments 및 /api/comments/{commentId}로 댓글 CRUD와 좋아요 기능을 관리할 수 있도록 했습니다. 회고이번 API 설계는 RESTful한 접근 방식을 유지하며, 간결하고 직관적인 구조를 갖도록 신경을 썼습니다. 테이블 간의 관계를 고려하여 API를 설계하는 과정에서 실무에서의 데이터 흐름과 REST API의 설계 원칙을 이해하는 데 도움이 되었습니다.아쉬운 점 및 보완할 점아직 JPA 관련 용어와 개념이 생소해서, 이해도가 높아지기까지 시간이 걸렸습니다. 다음 주에는 API 설계를 더욱 구체화하고, 실습하면서 느낀 궁금한 점들을 정리해 해결해 나갈 계획입니다.이번 주는 강의를 통해 프로젝트를 진행하고 API 설계를 하는 경험을 통해 많은 것을 배울 수 있었고, 다음 주에도 꾸준히 학습하며 부족한 부분을 보완해 나가겠습니다.

백엔드인프런워밍업클럽스터디백엔드프로젝트발자국회고SpringBoot

유선아

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

학습 했던 내용 요약자료구조 및 알고리즘 재귀 : 자기 자신을 참조하는 것, 탈출 조건 필수! (하노이탑)버블 정렬 : 앞에 있는 숫자와 옆에 있는 숫자를 비교해서 자리를 바꾸는 알고리즘.구현하기 쉽지만 성능O(n²)은 좋지 않음.선택 정렬 : 배열에 정렬되지 않은 영역에서 가장 작은 원소를 가장 첫번째로 가져온다. 완료된 영역은 더이상 참조하지않고, 정렬되지 않은 영역만 이 과정을 반복해 정렬하는 알고리즘.이해와 구현이 간단하지만, 성능O(n²)은 좋지 않음.  운영체제 프로그램을 실행시키면 메모리에 프로세스가 생성되고 각 프로세스에는 1개 이상의 스레드가 있다. 운영체제는 모든 프로세스에게 CPU를 할당, 해제하는데 이를 CPU 스케줄링이라 한다. 스케줄링 알고리즘에는 FIFO,SJF,RR, MLFQ 가 있다. FIFO (First In First Out) : 스케줄링 큐에 먼저 들어온 순서대로 CPU를 할당하는 방식이다. SJF (Shortest Job First) : Burst Time(작업시간)이 짧은 프로세스 먼저 실행되는 방식이다. RR (Round Robin) : 한 프로세스에게 일정 시간만큼 CPU를 할당하고, 할당 시간이 지나면 강제로 다른 프로세스에게 CPU 가 할당된다. 강제로 CPU를 뺏긴 프로세스는 큐의 가장 뒷부분으로 밀려나는 방식이다. MLFQ (Multi Level Feedback Queue) : 기본적으로 작은 크기의 타임 슬라이스를 선택하고, CPU 스케줄러에 의해 CPU가 뺏긴 상황이라면 , CPU Bound Process일 확률이 높으니 더 큰 타임 슬라이스를 할당 해주는 방식이다.공유된 자원에서 동기화 문제가 발생했고,이를 해결하기 위한 방법인 세마포어와 모니터방식이 있다. 공유자원 : 프로세스 간 통신을 할 때 공동으로 이용하는 변수나 파일1. 세마포어 : 운영체제가 가지고 있는 열쇠로, 공유자원을 한 프로세스만 이용하게끔 관리2. 모니터 방식 : 따로 운영체제가 처리하는 것이 아니라, 프로그래밍 언어 차원에서 지원하는 것.동기화 문제를 해결하기 위해 공유된 자원을 한 프로세스가 점유하게 만들었는데 교착상태(데드락) 발생교착상태 (데드락) : 여러 프로세스가 서로 다른 프로세스의 작업이 끝나기를 기다리다가 작업을 진행하지 못하는 상태교착상태가 발생하는 원인 : 공유자원해결방법 : 1. 가벼운 교착 상태 검출: 타이머 사용체크포인트를 만들어 저장하고, 타임 아웃시 마지막에 저장된 체크 포인트로 롤백2. 무거운 교착 상태 검출: 자원 할당 그래프 이용현재 운영체제에서 프로세스가 어떤 자원을 사용하는지 지켜보다가, 교착상태가 발생 시 해결순환 구조가 생긴 그래프를 통해 확인하고, 교착 상태를 일으킨 프로세스를 강제 종료시킨다. 회고일주일 동안 스스로 칭찬하고 싶은 점일중일치 진도대로 인강을 다 수강하고, 미션과 발자국도 기한내에 진행한 점.(인강을 수강하면서 정리한 내용)복습을 함께 진행한 점 아쉬웠던 점토요일까지로 미션과 발자국을 미리 완수하고 싶었는데, 그렇지 못한 점. 보완하고 싶은 점 이해가 어려웠던 부분들은 더 찾아보면서 이해해보기  다음주 학습 목표미션과 발자국을 마감기한보다 하루 일찍 완수하기   출처 : 그림으로 쉽게 배우는 운영체제 - 감자 , 그림으로 쉽게 배우는 자료구조와 알고리즘 (기본편)- 감자

알고리즘 · 자료구조워밍업클럽알고리즘CS발자국운영체제자료구조

양성빈

인프런 워밍업 스터디 클럽 2기 백엔드(클린코드, 테스트코드) 2주차 발자국

이 블로그 글은 박우빈님의 인프런 강의를 참조하여 작성한 글입니다.어느덧 인프런 워밍업 스터디 클럽을 시작한지도 2주째가 시작된다. 그리고 이번주 1주에 대한 회고를 시작해보려고 한다.이번주도 여러가지를 배우고 많은 경험이 된 한 주였다. 그럼 회고를 시작하겠다. 완주 및 우수러너를 위해 오늘도 달려본다.주석의 양면성클린코드 관점에서 주석은 죄악이냐 아니냐 논쟁이 많다.주석이 많다는 것은 그만큼 비즈니스 요구사항을 코드에 잘못 녹였다는 이야기코드를 설명하는 주석을 쓰면 코드가 아니라 주석에 의존한다. 주석에 의존하여 코드를 작성하면 적절하지 않은 추상화 레벨을 갖게 되어 낮은 품질의 코드가 만들어 진다. 🙋🏻 아니 그러면 주석 언제 써요?- 우리가 리팩토링 할때 정말 큰 난관 중 하나가 히스토리를 전혀 알 수 없는 코드다.- 후대에 전해야 할 "의사결정의 히스토리"를 도저히 코드로 표현할 수 없을 때 주석으로 상세히 설명한다.- 주석을 작성할 때 자주 변하는 정보는 최대한 지양해서 작성한다.- 만약 관련 정책이 변하거나 코드가 변경되었다면 주석도 잊지 않고 함께 업데이트 해야한다. 주석이 없는 코드보다 부정확한 주석이 달린 코드가 더 치명적이다.우리가 가진 모든 표현방법을 총동원해 코드에 의도를 녹여내고 그럼에도 불구하고 전달해야할 정보가 남았을때 사용하는게 주석이다.이번 예제 실습은 주석이 달린 gameStatus를 enum으로 변경하고 관련 비즈니스 로직을 MineSweeper가 아니라 GameBoard에 있는게 어울려 그곳으로 변경하였다.변수와 메서드의 나열 순서변수는 사용하는 순서대로 나열한다.인지적 경제성메서드의 순서도 고려해보아야 하는데 객체의 입장에서 생각하자.객체는 협력을 위한 존재이다. 외부세계에 내가 어떤 기능을 제공할 수 있는지를 드러낸다. (정해진 답은 아니지만 우빈님 추천) 공개 메서드들을 상단에 배치하고 나머지를 private 메서드들로 나열한다.공개 메서드들끼리도 기준을 가지고 배치하는 것이 좋다.객체지향을 하다보면 중요한 객체의 경우 메서드가 수십개까지도 늘어날 수 있는데 중요도 순, 종류별로 그룹화하여 배치하면 실수로 비슷한 조직의 메서드를 중복으로 만드는 것을 일관성 있는 로직을 유지할 수 있다.상태변경을 최우선, 그 이후는 판별, 조회로직이 있는 메서드들 순으로 한다.비공개 메서드는 공개 메서드에서 언급한 순서대로 배치한다.공통으로 사용하는 메서드라면 (가장 하단과 같이) 적당한 곳에 배치한다. 이를 통해 우리의 예시 코드도 이와 같이 리팩토링 하는 작업을 해보았다.패키지 나누기패키지는 문맥으로써의 정보를 제공할 수 있다.패키지를 쪼개지 않으면 관리가 어려줘 진다.패키지를 너무 잘게 쪼개도 마찬가지로 관리가 어려워진다.대규모 패키지 변경은 팀원과의 합의를 이룬 시점에 하자.현재 기준으로 본인만 변경하고 있는 부분이라면 괜찮으나 여러 사람이 변경중인 부분이나 공통으로 사용하는 클래스들의 패키지를 한번에 변경하면 추후 브랜츠 충돌이 날 수 있다.따라서 처음 만들때부터 잘 고민해서 패키지를 나누는 것이 좋다.  기능 유지보수하기 (1) - 버그 잡기해당 시간에는 깃발이 전부 꼽았을때 승리조건으로 가는 오류를 고쳐보았다. 이 수정을 통해 우리가 객체지향적으로 작성하여 수정될 곳이 적었지만 만약 이전의 코드였다면 여러군데 고쳐야 할 우려가 있었을 것이다.기능 유지보수하기 (2) - 알고리즘 교체하기 DFS(깊이 우선 탐색) -> 재귀, StackBFS(너비 우선 탐색) -> Queue재귀를 이용한 DFS도 결국 stack이다.스레드마다 생기는 스택영역에는 함수를 호출할 때마다 frame이 쌓인다.frame은 지역변수, 연산을 위한 정보등을 담고 있다.stack영역은 결국 크기가 제한되어 있다.우리가 필요한건 각 Cell의 모든 메서드 정보가 아니다. 각 Cell의 CellPosition만 있다면 원하는 작업을 할 수 있다. 그래서 해당 부분을 통해 우리는 재귀 로직을 stack형태로 변경을 해보았다.IDE의 도움 받기읽기 좋은 코드란 결국 가독성이 좋아야 한다. 이것을 위해 IDE의 큰 도움을 받을 수 있다.코드 포맷 정렬: option + cmd + L | Ctrl + Alt + L코드 품질: SonarLintlint: 잠재적인 문제가 될 수 있는 오류, 버그, 스타일등을 미리 알려주는 코드품질 체크도구포맷규칙: .editorconfig여러사람과의 협업을 염두하면 IDE의 기본 포맷팅에 익숙해지는 것이 좋다.스타일은 혼자 결정하는 것이 아니라 팀 내 합의로 도출되어야 한다.한번 정해지면 절재적인것이 아니라 사용하면서 계속 의견을 듣고 개선/반영하는 것이 좋다. 이런 기능들을 이용하여 우리 예제 프로젝트들도 포맷팅 및 리팩토링을 해보았다. ex) Stack -> Deque연습프로젝트 소개이번에는 새로운 도메인인 '스터디카페 이용 시스템'을 리팩토링하기 전 코드 해석을 하였다. 우빈님께서는 아래와 같은 사항을 중점으로 리팩토링을 해보시라고 하셨다.1. 추상화 레벨 (메서드 추출등)2. 객체로 묶어볼 만한 것은 없는지..3. 객체지향 페러다임에 맞게 객체들이 상호협력하고 있는지4. SRP: 책임에 따라 응집도 있게 객체가 잘 나뉘어져 있는지5. DIP: 의존관계 역전을 적용할만한 곳은 없는지6. 일급 컬렉션그래서 미션을 진행해보고 한번 강의를 학습해야겠다.미션3해당 미션을 하면서 조금은 많은 부분을 느꼈다. 일단 이렇게 클린코드 관점으로 코드를 리팩토링하는 것이 처음이기에 매우 익숙치 않았고 상당히 오래 걸렸다. 일단 나 나름대로 처음에 소개해준데로 리팩토링을 해보았자만 현재 코드에 대해 나름대로 만족을 한다.미션3 깃허브 링크리팩토링 (1) - 추상화 레벨해당 부분에는 예제 프로젝트에서 중복제거 및 메서드 추출 및 객체에 메세지를 보내어 getter방지를 해보았다. 내가 했던 미션과 비교를 해보니 이 부분은 대강 얼추 방향성을 잘 따란것 같다. 나는 여기서 추가적으로 라커 정책을 구현하였는데 이 부분은 내가 잘한 부분인지는 아직도 헷갈린다.리팩토링 (2) - 객체의 책임과 응집도이번 강의에서는 배울 점이 많았다. 나는 해당 설정 관련 부분들을 config에 빼고 해당 config를 getter로 삼는 provider로 넘겨주는 식으로 하였다. 하지만 I/O통합 부분은 진짜 강좌를 보면서 "아! 이것도 있었지.."라는 생각이 들며 조금은 반성이 되었다. 나머지 일급 컬렉션, display()의 책임 분리, Order객체로 분리하여 비즈니스 로직 이관까지는 그래도 비슷하게 갔던것 같다. 나는 거기서 조금 if문 3개로 나눠진 display를 switch문으로 변경까지 조금 읽기 쉬운 코드로 변경해보았다.리팩토링 (3) - 관점의 차이로 달라지는 추상화해당 부분은 나는 DIP 생각 없이 지뢰때 했던것 처럼 초기화 로직과 실행로직을 분리하고 이렇게 생각하니 FileHandler부분도 두개의 메서드를 분리할 수 있지 않을까 싶었고 그렇게 분리를 하였는데 강의에서는 DIP원칙을 적용하여 했었던것이다. 왜 그런지 모르고 그냥 기계처럼 한 것이 조금 반성스럽고 고쳐야할 부분이라 생각이 든다.능동적 읽기복잡하거나 엉망인 코드를 읽고 이해하려 할 때 리팩토링 하면서 읽기공백으로 단락 구분하기메서드와 객체로 추상화 해보기주석으로 이해한 내용 표기하며 읽기우리에게는 언제든 돌아갈 수 있는 git reset --hard가 있다.핵심목표는 우리의 도메인 지식을 늘리는 것 그리고 이전 작성자의 의도를 파악하는 것 이전까지 나는 코드를 눈으로 해석하고 리팩토링 하려는 습관들이 있었다. 하지만 이번 강의를 통해 코드를 분리해보고 주석도 달아보면서 리팩토링하면서 읽어가야겠다는 습관으로 고쳐야겠다는 생각이 들었다.오버 엔지니어링필요한 적정수준보다 더 높은 수준의 엔지니어링ex) 구현체가 하나인 인터페이스인터페이스 형태가 아키텍쳐 이해에 도움을 주거나 근시일내에 구현체가 추가될 가능성이 높다면 OK.구현체를 수정할때마다 인터페이스도 수정해야함.코드 탐색에 영향을 줌 / 어플리케이션이 비대해짐ex) 너무 이른 추상화정보가 숨겨지기 때문에 복잡도가 높아진다.후대 개발자들이 선대의 의도를 파악하기가 어렵다. 지금 이 내용을 학습해보니 이전에 미션3에서 내가 구현한 코드들이 오버엔지니어링이였지 않을까라는 생각을 하면서 반성하게 되었다.은탄환은 없다클린코드도 은탄환은 아니다.실무: 2가지 사이의 줄다리기지속가능한 소프트웨어의 품질 vs 기술부채를 안고 가는 빠른 결과물대부분의 회사는 돈을 벌고 성장해야하고 시장에서 빠르게 살아남는 것이 목표이런 경우에도 클린코드를 추구하지 말라는 것이 아니라 미래시점에 잘 고치도록 할 수 있는 코드센스가 필요하다. 결국은 클린코드의 사고법을 기반으로 결정하는 것모든 기술의 방법론은 적정 기술의 범위 내에서 사용ex) 당장 급하게 배포나가야 하는데 동료에게 style관련된 리뷰를 주고 고치도록 강요하는 사람도구라는 것은 일단 그것을 한계까지 사용할 줄 아는 사람이 그것을 사용하지 말아야 할때도 아는 법이다.적정 수준을 알기 위해 때로는 극단적인 시도도 필요하다. 이것을 보고 미션때 오버 엔지니어링을 해보는것도 좋은 경험이 되었다고 다시 느끼게 되었다.📚 기술부채란?현 시점에서 더 오래 소요될 수 있는 더 나은 접근방식을 사용하는 대신 쉬운(제한된) 솔루션을 채택함으로써 발생되는 추가적인 재작업의 비용을 반영하는 소프트웨어 개발의 한 관점마무리하며드디어 해당 강의가 마무리 되었다. 여기서 가장 핵심은 추상이다. 우리는 또한 추상과 구체를 인식할 수 있다. 김창준님께서 집필하신 '함께자라기'라는 책을 보면 알듯이 추상과 구체를 넘나들어야 한다. 때로는 bottom-up 때로는 top-dowon을 사용하면서 추상적인 시각과 구체적인 시각을 자유롭게 사용해보고 조금 더 읽기 쉽고 좋은 코드를 작성하는 개발자가 되어야 겠다는 생각이 들었다.Day4 공통 피드백내 코드가 예시로 나왔다 우빈님께서 해주신 말씀은 아래와 같았다.1. boolean으로 return하고 있는 메서드에 예외를 발생시키는데 시도는 좋으나 항상 메서드의 사용현황을 파악 후 상황에 맞게 리팩토링을 하는 것이 좋다. 또한 예외 던지는 것은 비싸기에 항상이 아닌 신중하게 하시라고 조언을 주셨다.2. 추출한 메서드의 static 키워드가 존재한다면 인텔리제이 IDE에서 메서드 추출을 하면 자동으로 붙기에 알아서 제거해줘야 한다.3. 상황에 맞게 적절한 수준의 리팩토링이 좋다. 너무 자세히 가면 오버 엔지니어링이 된다.Day7 코드리뷰내가 만든 코드에서 StudyCafeConfigProvider라는 객체를 만들고 사용중이였는데 아래와 같이 이 안에는 전부 static 메서드만 있었다.public class StudyCafeConfigProvider {     private static final StudyCafeConfig CONFIG = new StudyCafeConfig();     public static InputHandler getInputHandler() {         return CONFIG.getInputHandler();     }     public static OutputHandler getOutputHandler() {         return CONFIG.getOutputHandler();     }     public static StudyCafeSeatReadProvider getStudyCafeSeatReadProvider() {         return CONFIG.getStudyCafeSeatReadProvider();     }     public static StudyCafeLockerReadProvider getStudyCafeLockerReadProvider() {         return CONFIG.getStudyCafeLockerReadProvider();     }     public static Map<StudyCafePassType, StudyCafePassType> getStrategyMap() {         return CONFIG.getStrategyMap();     }     public static Map<StudyCafePassType, LockerPolicyType> getLockerPolicyMap() {         return CONFIG.getLockerPolicyMap();     } }이런 경우에는 private constructor를 만드는것이 좋다고 SonarLint에서 알려준다. 하지만 내 인텔리제이에서는 SonarLint를 적용되어 있지만 경고가 따로 뜨지 않았는데 이 부분은 한번 자세히 살펴봐야겠다.자세한 리뷰 및 후기는 추가 포스팅을 하여 정리해봐야겠다.후기이번 주도 금방 지나갔다. 리뷰를 들으면서 많은 고민과 생각이 들었다. 또한 다른 러너분들의 코드를 보면서 신박한 생각과 좋은 점들이 눈에 보이기 시작했다. 우빈님이 주신 피드백과 다른분들의 코드중에 좋은 점들을 채택해서 더 좋은 코드들로 한번 리팩토링을 다시금 해봐야겠다. 다음주부터는 테스트 강의의 시작이다. 테스트도 조금은 걱정이 되지만 열심히 해서 조금 더 성장하는 주가 되었으면 하는걸로 마무리를 지어보겠다.

백엔드인프런워밍업스터디클럽2기백엔드클린코드테스트코드발자국

 집사

첫번째 발자국

'그림으로 쉽게 배우는 자료구조와 알고리즘 (기본편)'수강생 여러분께 하고 싶은 말외우려 하지 말고 이해해라 어렵다면 그림으로 풀어서 이해해라당장 이해하기 어렵다면 특징만 외우고 나중에 다시 공부하기이해를 했다면 기억도 오래 남고 특징들을 유추할 수 있다자료구조와 알고리즘이란?자료구조는 데이터가 어떤 구조로 저장되고 사용되는지를 나타낸다. (ex. int, float, 정적배열, 동적배열, 연결리스트 등)알고리즘은 어떤 문제를 해결하기 위한 확실한 방법이다.자료구조에 따라 알고리즘이 달라진다.어떤 구현을 할 때 하나의 자료구조가 하나의 알고리즘만을 사용할 수 있는건 아니다. 상황에 맞는 적절한 자료구조와 알고리즘을 적용할 수 있어야 한다.시간복잡도더 좋은 알고리즘이란 무엇일까? 이는 사용자의 요구에 따라 변한다. 보통 메모리, 속도로 구분되며 일반적으로 알고리즘의 속도를 성능의 척도로 사용시간복잡도란 특정 알고리즘이 어떤 문제를 해결할 때 걸리는 시간이며 사용자의 PC성능에 따라 시간 측정은 달라질 수 있으므로 코드에서 성능에 많은 영향을 주는 것을 찾아 실행 시간을 예측하는 것이다.시간복잡도는 최악의 경우를 표현하는 빅 오 표기법을 사용한다.빅 오 표기법은 가장 큰 영향을 미치는 항으로만 표현한다.보통 자료구조의 시간복잡도는 평균, 최악의 경우를 생각한다.자료구조배열연속된 메모리 공간을 할당 받는다.운영체제는 배열의 시작 주소만 기억한다.순차적으로 메모리가 적재되고 운영체제가 배열의 시작 주소를 알기에 인덱스를 통해 접근 가능하다.삽입, 삭제 시 공간이 부족하거나 중간에 있는 요소를 삭제 시 데이터에 대한 이동이 필요해서 오버헤드가 많이 발생한다.인덱스 참조 O(1) / 삭제, 삽입 성능 O(n)연길리스트배열의 단점을 해결하기 위해 만들어진 자료구조저장하려는 데이터들을 메모리 공간에 분산하여 할당하고 이 데이터들을 서로 연결이는 노드라는 것을 만들어 수행노드의 구조는 데이터를 담는 변수 하나와 다음 노드를 가리키는 변수 하나이러한 노드끼리 연결시킨것을 연결리스트라 한다.연결리스트는 첫 노드의 주소만 알고 있으면 다른 모든 노드에 접근 가능.삽입, 삭제시 다음 가리키는 노드만 바꿔주면 된다. / 삽입, 삭제 O(1)메모리 공간이 분산되어 있기에 인덱스 참조가 불가능 즉, 모든 노드 순회해야함 / 참조 O(n)스택First In Last Out (FILO)먼저 들어간 데이터가 나중에 나오는 자료구조삽입, 삭제, 참조 O(1) / 맨 위에 요소만 가능연결리스트, 배열 등으로 구현 가능사용 예제명령 / Undo, Redo문법 괄호 검사큐와 병행하여 회문 검사큐First In First Out (FIFO)먼저 들어간 데이터가 먼저 나오는 자료구조삽입, 삭제, 참조 O(1) / 맨 앞에 요소만 가능이중 연결리스트, 배열등으로 구현 가능사용예제대기열 / 마트 계산대, 게임 큐, 식당 줄 등운영체제 프로세스 작업 요청 / FIFO 스케줄링덱데이터의 삽입과 제거를 Head와 Tail 양쪽에서 자유롭게 할 수 있는 자료구조양방향 끝 삽입 삭제, 참조 O(1) / 가운데 O(n)해시테이블Key와 Value로 이루어진 자료구조Key를 이용한 해시함수를 통해 데이터를 저장만약 해시 충돌이 발생할 경우, 해당 인덱스의 연결리스트에 삽입해시함수의 구현에 따라 공간의 낭비가 극대화될 수도 최적화될 수도 있다.최고의 효율 : 참조, 삽입, 삭제 O(1)최악의 효율 : 참조, 삽입, 삭제 O(n)셋데이터의 중복을 허용하지 않는 자료구조해시 테이블을 활용하기에 해시 셋이라고도 불린다.셋은 헤시 테이블의 Value값은 사용하지 않고 Key만 사용해 구현한다.Key가 Key인 동시에 데이터로 사용하는 것'그림으로 쉽게 배우는 운영체제'운영체제 개요프로세스 관리 메모리 관리 하드웨어 관리 파일 시스템 관리운영체제의 구조운영체제의 핵심 커널은 프로세스와 메모리, 저장장치를 관리한다.사용자는 커널에 직접 접근할 수 없고 인터페이스를 통해 접근 가능하다. (GUI, CLI)어플리케이션은 시스템 콜을 통해 커널에 접근 가능하며, 이를 통해 메모리를 사용할 수 있다.하드웨어는 드라이버를 통해 커널에 접근 가능하다. 컴퓨터 하드웨어와 구조하드웨어로 프로그램을 만들었기에 프로그램이 달라질 때마다 매번 스위치와 배선을 다시 조정해야 했다.폰 노이만은 이를 해결하기 위해 CPU와 메모리를 두고 이들 사이는 버스로 연결한다.버스는 데이터를 전달하는 통로이다.메모리에 올라간 프로그램은 명령에 따라 처리된다.컴퓨터 하드웨어메인보드다른 하드웨어를 연결하는 장치장치 간에 데이터를 전송하는 건 메인보드의 버스가 담당.CPU메모리하드디스크그래픽카드마우스, 키보드, 사운드, 모니터 입출력 장치  CPU(Central Processing Unit)산술논리연산장치(Arithmetic and Logic Unit, ALU) : CPU 에서 실제로 데이터 연산을 담당제어장치 : 모든 장치들의 동작을 지시하고 제어레지스터 : CPU 내에서 계산을 위해 데이터를 임시로 보관하는 장치 메모리 종류RAM(Random Access Memory)랜덤으로 데이터를 읽어도 저장된 위치와 상관 없이 읽는 속도가 같다.전력이 끊기면 데이터를 잃는다(휘발성) / 메인 메모리로 사용ROM(Read Only Memory)전력이 끊겨도 데이터를 계속 보관 가능데이터를 한 번 쓰면 수정 불가능컴퓨터의 부팅과 관련된 바이오스를 저장하는데 사용컴퓨터의 부팅과정ROM에 저장된 BIOS 실행BIOS는 전원, CPU, 메모리, 키보드, 마우스, 하드디스크 등 주요 하드웨어에 이상이 없는지 확인하드디스크에 있는 마스터 부트 레코드에 저장된 부트로더를 메모리에 가져와 실행설치된 운영체제 실행, 메모리에 불러온다.바탕화면이 나오고 실행되는 모든 응용 프로그램은 메모리에 올라가 운영체제가 관리인터럽트입출력 처리 방식폴링CPU가 주기적으로 입출력 장치의 상태를 확인하는 방식효율성이 떨어지고 자원 낭비 심함인터럽트폴링 방식을 개선한 현재 사용되는 방식입력이나 출력이 발생하면 CPU에 인터럽트를 발생CPU는 현재 작업을 중단하고, 이 인터럽트를 처리하기 위해 인터럽트 처리 루틴(Interrupt Service Routine, ISR)로 이동 및 처리완료 후 다른 작업 수행프로세스와 쓰레드프로그램과 프로세스프로그램은 저장장치에 저장된 명령문의 집합체프로세스는 프로그램이 메모리에 올라가 실행중인 프로그램을 의미멀티프로그래밍과 멀티프로세싱멀티프로그래밍은 메모리에 여러 개의 프로세스가 올라간 것.멀티프로세싱은 CPU를 시분할로 여러 개의 프로세스를 처리하면서 동시에 실행되는 것처럼 보이게 하는 것.과거에는 메모리가 작기에 멀티프로그래밍이 불가하여 다른 저장장치에 있는 프로그램을 메모리에 올리는 스와핑을 통해 멀티프로세싱을 처리했다.PCB프로세스가 생성될 때 운영체제는 해당 프로세스의 정보를 가지고 있는 PCB(Process Control Block)를 만들어 저장한다.운영체제는 PCB들을 연결리스트로 관리한다.PCB의 구조포인터 : 부모와 지식 프로세스에 대한 포인터 / 할당된 자원에 대한 포인터 / 프로세스 상태 전환시 저장하는 포인터프로세스 상태 : 생성, 준비, 실행, 대기, 완료프로세스 ID : 식별자프로그램 카운터 : 다음에 실행될 명령어의 주소 저장 / 프로세스가 실행되던 지점 저장레지스터 정보 : 프로세스가 실행될 때 사용했던 레지스터 값들메모리 관련 : 프로세스가 메모리에 있는 위치 정보, 메모리 침범을 막기 위한 경계레지스터 값등 저장CPU 스케줄링 정보 : CPU 스케줄링에 필요한 우선순위, 최종 실행시간, CPU 점유시간 등이 저장등등프로세스 상태운영체제는 시분할 시스템을 활용하여 여러 개의 프로세스를 빠르게 전환하며 실행시킨다.시분할 처리를 위한 다섯가지 상태생성(New) : PCB를 생성하고 메모리에 프로그램 적재를 요청한 상태준비(Ready) : CPU를 사용하기 위해 기다리는 상태대기(Waiting) : 프로세스가 입출력 요청을 하면 입출력이 완료될 때 까지 기다리는 상태 / CPU는 굉장히 빠르고 입출력 작업은 상당히 느리다. 입출력 요청을 하는 프로세스가 완료될 때 까지 CPU를 기다리게 하는 것은 비효율적이기에 대기 상태가 만들어졌다.실행(Running) : CPU 스케줄러에 의해 CPU를 할당받아 실행되는 상태 / 실행상태에 있는 프로세스의 수는 CPU의 개수만큼 존재할 수 있다.완료(Terminated) : 프로세스가 종료된 상태 / 프로세스가 사용했던 데이터를 메모리에서 제거하고 PCB도 제거 컨텍스트 스위칭컨텍스트 스위칭은 프로세스를 실행하는 중에 다른 프로세스를 실행하기 위해 실행중인 프로세스 상태를 저장하고 다른 프로세스의 상태값으로 교체하는 작업을 의미.컨텍스트 스위칭이 일어날 때 PCB의 내용이 변경된다.실행중인 프로세스의 작업내용을 PCB에 저장하고 실행될 프로세스의 PCB의 내용대로 CPU가 다시 세팅된다.컨텍스트 스위칭이 일어날 때 PCB에 변경되는 값들은 아래와 같다.프로세스 상태프로그램 카운터레지스터 정보메모리 관련 정보 프로세스의 CPU 점유시간을 초과하거나 입출력 작업요청 등이 들어오면 인터럽트가 발생하며 컨텍스트 스위칭이 일어난다.프로세스 생성과 종료실행파일이 실행되면 운영체제는 해당 프로그램의 코드영역과 데이터영역을 메모리에 로드하고빈 스택과 빈 힙을 만들어 공간을 확보하며 이 프로세스를 관리하기 위한 PCB를 만들어서 값을 초기화해준다.해당 프로세스 생성 과정은 운영체제가 부팅되고 0번 프로세스가 실행될 때 딱 한번 실행된다.그 이후에 프로세스는 새로 생성하지 않고 0번 프로세스를 복사(fork함수)해서 사용한다.새로 생성하는 것 보다 복사를 하는 게 더 빠르다.exec함수를 통해 부모를 복사한 자식 프로세스의 코드와 데이터 영역을 원하는 값으로 덮어쓴다.exit함수는 자식 프로세스가 부모 프로세스에게 정상 종료를 알리는 함수이다. / 부모 프로세스의 경우 종료부모 프로세스는 자식 프로세스의 Exit Status를 읽고 자식 프로세스를 정리한다.만약 부모 프로세스가 자식 프로세스보다 먼저 종료 되거나 자식 프로세스가 비정상적으로 종료되어 exit()신호를 주지 못해서 Exit Status를 읽지 못해 메모리에 계속 살아 있는 상태를 좀비 프로세스라고 한다.컴퓨터를 오래 켜두면 느려지는 현상이 발생하곤 하는데 메모리에 많은 프로세스가 올라오는 경우거나 좀비 프로세스가 메모리를 차지하기 때문이다.컴퓨터를 껏다키면 메모리가 초기화 되기에 다시 빨라진다.쓰레드사용자가 운영체제에게 작업을 요구하면 그만큼 프로세스 수가 증가프로세스는 메모리에 코드, 데이터, 스택, 힙영역, PCB를 할당해준다.프로세스끼리의 통신은 IPC(Inter Process Comunication)를 이용 / 해당 작업은 비용이 많이 든다.이러한 단점들을 해결하기 위해 고안된 것이 쓰레드이다.쓰레드는 프로세스 내에 존재하는 것으로 1개 이상이 있을 수 있다.쓰레드는 프로세스의 PCB, 코드, 데이터, 힙영역을 공유한다.스택은 공유하지 않고 쓰레드 마다 고유하다.한 프로세스 내에 쓰레드가 여러개 존재하기에 쓰레드 ID, TCB(Thread Control Block)가 생겼다.이제 운영체제가 작업을 처리하는 단위는 프로세스 내에 쓰레드이다.특징안전성 : 프로세스는 서로 독립적이기에 하나의 프로세스가 문제가 있더라도 다른 프로세스는 영향을 받지 않는다.반면 쓰레드는 하나의 프로세스 내에 존재하기에 해당 프로세스에 문제가 생기면 그 안에 모든 쓰레드에 문제가 생긴다.속도, 자원 : 각각의 프로세스는 서로 고유한 자원을 가진다 / 코드, 데이터, 힙, 스택영역을 전부 따로 둔다. 프로세스간의 통신을 하려면 IPC를 이용해야해서 오버헤드가 크고 속도가 느리다.반면 쓰레드는 한 프로세스내에서 스택영역을 제외한 영역은 모두 공유하기에 오버헤드가 굉장히 작다. 쓰레드간의 통신은 데이터를 공유할 수 있으니 쉽게 가능하지만 공유되는 공간에서 문제(공유자원 문제)가 발생할 수 있다. CPU 스케줄링CPU 스케줄링 개요CPU 스케줄링은 운영체제가 모든 프로세스에게 CPU를 할당/해제하는 방식을 의미한다.CPU 스케줄링에서 스케줄러(운영체제)가 고려해야할 사항은어떤 프로세스에게 CPU 리소스를 줘야하는가?CPU를 할당받은 프로세스가 얼마의 시간동안 CPU를 사용해야하는가?두 가지이다.이 두 가지 고려사항이 컴퓨터의 성능에 굉장히 큰 영향을 미친다.이 고려사항을 통해 여러 CPU 스케줄링 방식이 만들어진다.CPU를 할당받아 실행하는 작업을 CPU Burst라 부른다.입출력 작업을 I/O Burst라고 부른다.다중큐해당 프로세스의 우선순위를 보고 준비큐에 넣는다.CPU 스케줄러는 준비상태의 다중큐에 들어있는 프로세스들 중에 적당한 프로세스의 정보(PCB)를 선택해서 실행상태로 전환시킨다프로세스 정보를 담고 있는 PCB가 준비상태의 다중큐에 들어가서 실행되기를 기다리고 있고CPU 스케줄러에의해 실행상태로 전환된다.이때 CPU 스케줄러는 준비상태의 다중큐를 참조해서 어떤 프로세스를 실행시킬지 결정.스케줄링 목표리소스 사용률 : CPU 사용률을 높이는 것, I/O 디바이스 사용률 높이는 것오버헤드 최소화 : 스케줄링을 위한 계산, 컨텍스트 스위칭 오버헤드 비용 최소화공평성 : 모든 프로세스에게 스케줄링 기법에 맞춰 공평하게 CPU가 할당되어야 한다.처리량 : 같은 시간 내에 더 많은 처리를 할 수 있는 방법을 목표로 한다.대기시간 : 작업을 요청하고 실제 작업이 이루어지기 전까지 대기하는 시간이 짧은 것을 목표로 한다.응답시간 : 응답시간이 짧은 것을 목표로 한다.모든 목표를 만족할 수 없기에 사용자가 사용하는 시스템에 따라서 목표를 다르게 설정해야 한다.특별한 목적이 없을 경우, 밸런스를 유지하는게 중요.FIFO먼저 들어온 작업이 먼저 처리되는 스케줄링 방식스케줄링 큐에 들어온 순서대로 CPU를 할당받는 방식 / 해당 방식은 프로세스가 완전히 끝나야만 다음 프로세스가 실행될 수 있다.단점실행중인 프로세스가 완전히 끝나야 다음 프로세스가 실행되는데, 만약 현재 실행중인 프로세스 작업이 길고, 다음 프로세스가 엄청 짧아도, 다음 프로세스는 대기해야 한다. / 효율성(처리량, 대기시간)이 떨어진다.I/O 작업이 있다면 해당 작업이 끝날때까지 CPU가 쉬게되어 CPU 사용률이 떨어진다.스케줄링의 성능은 평균 대기 시간으로 평가된다.평균 대기 시간은 프로세스들 모두가 실행되가끼지 대기시간의 평균을 의미한다. Burst Time이 짧은게 먼저 실행되면 평균 대기 시간 짧아짐.Burst Time이 긴게 먼저 실행되면 평균 대기 시간 길어짐. FIFO알고리즘은 프로세스의 Burst Time에 따라 성능의 차이가 심하게 나기에 현대 운영체제에서 잘 쓰이지 않고 일괄처리 시스템에 쓰인다. 

감자인프런강의발자국

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

수강 방식전체적으로 강의 내용을 노션에 정리하며 수강(비공개 처리)강의 화면을 캡쳐하여 정리하고 코드를 따라치지만 단순 클론 코딩이 아닌 이해될 때 까지 넘어가지 않음) 운영체제 비전공자 출신에게 도움이 되는 내용들이었습니다. 프로세스, 쓰레드나 메모리, cpu등등 말로 듣고 글로 된 설명을 읽었을 때는 제대로 이해되지 않았던 부분들이 그림과 함께 학습하니 이해가 빠르게 되었습니다.외에도 프로그램이 어떤 흐름을 통해 실행되는지, 인터넷 창에서 생각없이 눌렀던 탭 추가 기능은 스레드를 추가함으로써 과도한 메모리 할당을 막는다는지 등등이 있었습니다.아직까지는 많은 내용을 보지 못했지만 남아있는 강의들을 수강한다면 실무에 도움이 크게 될거라 생각하여 열심히 수강해야 겠다는 생각을 가졌습니다.   자료구조와 알고리즘 C로 배우고 C#으로 개발하고 있는 나에게 자바스크립트는 익숙하면서도 낯선 언어였습니다. 다만 다뤄보았던 자료구조는 같았기에 이해가 어렵진 않았습니다.다만 연결리스트의 개념 이해와 구현은 조금 어려웠습니다.처음에는 head와 next가 뭔지 이해가 되지 않았으나 그림을 보고 코드를 따라치다보니 조금씩 이해가 되었던 것 같습니다.연결 리스트를 통해 큐, 스택, 덱 등등 자료구조들을 직접 구현해보는 점이 좋았고 현실에서 자료구조와 비슷한 경험을 빗대어 말씀해주시는게 좋았습니다.특히나 자료구조는 실무 개발에서도 적용할 수 있기 때문에 지금까지 배운 자료구조와 앞으로 배울 자료구조 및 알고리즘을 수강하면서 하나씩 적용해보고자 합니다.   

발자국

suover

인프런 워밍업 클럽 스터디 2기 - 백엔드 프로젝트 1주차 발자국

입문자를 위한 Spring Boot with Kotlin - 나만의 포트폴리오 사이트 만들기강의와 함께한 인프런 워밍업 클럽 스터디 2기 - 백엔드 프로젝트 (Kotlin, Spring Boot) 1주차 발자국 입니다. 강의 수강이번 주에는 웹 개발의 기본 개념을 학습하며, Spring Boot를 중심으로 한 웹 개발에 대한 전반적인 내용을 다루었습니다. 특히, 웹 서비스의 구성 요소인 클라이언트, 서버, 데이터베이스의 상호작용 방식을 이해하고, 클라이언트와 서버가 데이터를 주고받는 과정에서 발생하는 HTTP 통신과 REST API의 개념을 명확히 정리했습니다.Spring Boot를 활용한 프로젝트 구조와 레이어드 아키텍처(Controller, Service, Repository)를 학습하며, 각 레이어의 역할과 책임에 대해 더욱 깊이 이해할 수 있었습니다. 또한, MVC 패턴과 디자인 패턴의 중요성도 학습하며, 어떻게 하면 코드의 결합도를 낮추고 유지보수성을 높일 수 있는지 고민하는 시간을 가졌습니다. 회고일주일간 강의를 들으면서 웹 개발의 전반적인 흐름과 기본 개념을 더 체계적으로 정리할 수 있었습니다. 이전에도 어느 정도 개념을 알고 있었지만, 이번에는 MVC 패턴이나 레이어드 아키텍처의 필요성에 대해 더 명확히 이해하게 되어 좋았습니다. 프레임워크와 라이브러리의 차이도 명확해져, 앞으로 어떤 상황에서 어떤 도구를 선택할지에 대한 기준이 생긴 것 같아 뿌듯합니다.칭찬할 점매일 꾸준히 강의를 들으며 웹 개발의 기본기를 다진 점학습한 내용을 직접 실습 프로젝트에 적용하면서 이론과 실제를 연결 지은 점이번 주 계획했던 학습 목표를 모두 달성한 점 아쉬웠던 점스프링의 의존성 주입(Dependency Injection)에 대한 이해가 조금 부족하다는 느낌이 있었습니다. 강의에서 개념을 배웠지만, 실제로 이를 프로젝트에 어떻게 적용해야 하는지 고민하는 시간이 좀 더 필요할 것 같습니다. 보완할 점의존성 주입을 주제로 더 깊이 파고들어 실습해보고, 관련된 자료를 찾아보면서 이해도를 높여야겠습니다. 특히, 생성자 주입 방식을 더 명확하게 습득하고 싶습니다. 다음 주 목표다음 주에는 이번 주에 학습한 내용을 바탕으로 더 구체적인 프로젝트를 진행하며, 의존성 주입과 스프링 빈 관리에 대한 이해를 심화할 계획입니다. 이를 통해 Spring Boot의 기능을 제대로 활용할 수 있도록 하겠습니다.미션이번 주 미션은 미니 프로젝트 개발을 주제로, 웹 개발의 기본적인 데이터베이스 테이블을 설계하고 이를 GitHub에 업로드하는 것이 목표였습니다. 프로젝트의 주제는 다양한 활동에서 함께할 사람을 찾고 소통할 수 있는 게시판 서비스로, 사용자, 카테고리, 게시글, 댓글, 그리고 좋아요 기능을 포함한 테이블을 설계했습니다. 미션 과정테이블 설계 사용자 테이블 (users): 사용자의 이메일, 비밀번호, 이름, 닉네임 등을 저장하며, user_role 필드로 사용자 권한을 설정할 수 있습니다.카테고리 테이블 (categories): 게시글의 카테고리를 관리하기 위해 설계된 테이블로, 카테고리 이름을 저장할 수 있습니다.게시글 테이블 (posts): 사용자가 작성한 게시글을 저장하는 테이블로, 사용자와 카테고리와의 관계를 참조하도록 했습니다.댓글 테이블 (comments): 게시글에 달린 댓글을 저장하는 테이블로, 답글까지 고려한 설계를 진행했습니다.좋아요 테이블 (post_likes, comment_likes): 게시글과 댓글에 대한 '좋아요' 기능을 구현하기 위한 테이블을 설계했습니다.  프로젝트 깃허브에 업로드미니 프로젝트를 깃허브에 올리는 작업을 진행했습니다. 이번 작업을 통해 버전 관리와 협업의 중요성을 다시 한번 확인할 수 있었습니다. 회고테이블 설계를 처음부터 끝까지 진행하면서, 데이터베이스 간의 관계를 설계하는 것이 얼마나 중요한지 다시 한번 깨달았습니다. 또한, 테이블 관계를 명확히 이해하지 못했던 부분에서 여러 번 수정을 거치면서 실무적인 감각도 더 키울 수 있었습니다. 특히, 외래 키 제약 조건을 걸어 테이블 간의 참조 무결성을 유지하는 방법을 다시 복습하게 된 것이 큰 수확이었습니다.아쉬운 점테이블 설계를 처음 진행할 때, 부모-자식 관계나 외래 키 제약 조건을 제대로 설정하지 못해 몇 번의 수정을 반복해야 했습니다. 이를 통해 초기 설계의 중요성을 다시 한번 느끼게 되었고, 더 많이 연습해야 한다는 생각이 들었습니다.보완할 점초기 설계 단계에서 더 철저하게 준비하여, 데이터베이스 구조를 명확하게 하고 오류를 최소화하는 방향으로 나아갈 계획입니다.이번 주는 강의와 미션 모두에서 많은 것을 배웠고, 실습을 통해 이론을 실제 프로젝트에 적용하면서 자신감을 얻었습니다. 다음 주에도 꾸준히 학습하며 부족한 부분을 보완해 나갈 계획입니다.

백엔드인프런워밍업클럽스터디백엔드프로젝트발자국회고SpringBoot

karl

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

지난 일주일 동안의 강의 내용 중 일부를 발췌했습니다. 자세한 내용은 개인 노션 페이지에 타이핑했습니다.자료구조와 알고리즘자료구조: 데이터가 어떤 구조로 저장되고 어떻게 사용되는지를 나타냄 예시: 변수, 배열알고리즘: 어떤 문제를 해결하기 위한 확실한 방법자료구조에 따라 알고리즘이 달라짐. 즉, 자료구조에 많은 영향을 받음같은 자료구조더라도 여러 알고리즘이 있을 수 있음시간 복잡도: 특정 알고리즘이 어떤 문제를 해결하는 데 걸리는 시간특정 알고리즘의 성능을 평가할 때는 해당 알고리즘의 반복문을 보고 성능을 평가스택: First In Last Out(FILO): 먼저 들어간 데이터가 나중에 나온다.큐: First In First Out(FILO): 먼저 들어간 데이터가 먼저 나온다.덱: 데이터의 삽입과 제거를 head와 tail 두 군데서 자유롭게 할 수 있는 자료구조스택은 head에서만 처리할 수 있고, 큐는 head에서 삽입, tail에서 제거한다.해시테이블: 해시와 테이블이 합쳐진 자료구조테이블의 메모리를 적게 차지하고, 충돌이 덜 일어나도록 데이터를 골고루 분배시키는 좋은 해시 함수의 구현이 필수적운영체제커널프로세스와 메모리, 저장 장치를 관리하는 핵심적인 기능을 담당사용자는 커널에 직접 접근할 수 없으며, GUI/CLI를 통해 접근함 CPU를 구성하는 장치산술 논리 연산 장치: CPU에서 실제로 데이터 연산을 담당하는 장치제어 장치: 모든 장치들의 동작을 지시하고 제어하는 장치레지스터: CPU 내에서 계산을 위해 임시로 보관하는 장치메모리 종류램(RAM): 랜덤으로 데이터를 읽어도 저장된 위치와 상관없이 읽는 속도가 같음. 전력이 끊기면 데이터를 잃어버리기 때문에 메인 메모리로 사용롬(ROM): 전력이 끊겨도 데이터를 계속 보관할 수 있지만, 데이터를 한번 쓰면 수정할 수 없으므로, 컴퓨터의 부팅과 관련된 바이오스를 저장하는데 주로 사용멀티프로그래밍메모리에 여러 개의 프로세스가 올라온 것 멀티프로세싱유니프로그래밍, 멀티프로그래밍을 메모리 관점에서 정의했다면, 멀티프로세싱은 CPU 관점으로 정의한 것CPU가 여러 개의 프로세스를 처리하는 것컨텍스트 스위칭프로세스를 실행하는 중에 다른 프로세스를 실행하기 위해 실행 중인 프로세스의 상태를 저장하고 다른 프로세스의 상태 값으로 교체하는 작업컨텍스트 스위칭이 일어날 때 PCB의 내용이 변경된다.FIFO(First In First Out)먼저 들어온 작업이 먼저 나간다는 뜻으로, 스케줄링 큐에 들어온 순서대로 CPU를 할당받는 방식먼저 들어온 프로세스가 완전히 끝나야만 다음 프로세스가 실행될 수 있다.회고잘한 점강의 계획 일정에 따라 밀리지 않고 들었다. 애매했던 개념을 잘 정리할 수 있어서 좋았다.아쉬운 점정해진 일정의 강의만 생각하다보니 생각의 유연함이나 확장이 부족했다. 주말 동안에 시간을 내서 보충했는데, 평일에 강의를 들으면서 조금만 더 시간을 투자하면 주말엔 다른 공부나 휴식하는 데 쓸 수 있을 듯하다. 목표꾸준히 강의 듣고 학습하기강의를 보면서 타이핑한 내용을 한 번만 보는 게 아니라 두 세번 보면서 이해하기이전에 배웠던 내용도 꾸준히 복습하기미션(https://inf.run/yyYhZ)미션을 해결한 과정한 주 동안의 강의 내용을 정리한 것을 보면서 미션을 해결했다. 강의에서는 나오지 않았지만, 비슷한 예시는 해당 강의를 다시 보면서 힌트를 얻었다. 회고강의와 똑같은 내용이 미션에 나오기보다, 비슷한 유형을 낸 미션이었다. 처음 만났을 땐 막막했지만, 강의 내용을 되새겨보면서 힌트를 얻을 수 있었고, 해결했다. 이후 미션에 어떤 문제가 나올지 모르겠지만, 최대한 적용할 수 있도록 강의를 한 번 보고 끝내는 게 아니라 이해할 수 있도록 노력해야겠다.

발자국워밍업클럽cs

채널톡 아이콘