블로그

양성빈

[인프런 워밍업 스터디 클럽] 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 들은 자바부터 상세히 파보기 시작했습니다. 그러면서 여러 자료를 찾다가 다른 러너분들과 공유되고 싶다는 글을 찾게 되었습니다. 이렇게 첫 시작으로 지금 마음가짐으로 끝까지 완주해서 우수러너까지 노려봤으면 좋겠습니다. 다들 응원 부탁드립니다. 🥳 📚 참고자료오라클 블로그 

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

이양구

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

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

이양구

[인프런 워밍업 클럽 FE 0기] 미션7 - 예산 계산기 앱

💸 Budget Calculator APP GitHub 💸 Budget Calculator APP DemoRecord by ScreenToGif  개요인프런 워밍업 클럽 FE 0기의 일곱 번째 미션인 '예산 계산기 앱' 입니다. 따라하며 배우는 리액트 섹션 0~3(To-Do 앱) 목표의존성 배열(Dependency Array) 을 이용해 함수 실행하기state 를 전역 변수처럼(?) 사용해보기 구현구조|-- App | |-- Form | |-- Lists | | |-- List  의존성 배열(Dependency Array) 을 이용해 함수 실행하기// App.jsx const [budgetList, setBudgetList] = useState( JSON.parse(localStorage.getItem("budgetList")) || [] ); useEffect(() => { localStorage.setItem("budgetList", JSON.stringify(budgetList)); }, [budgetList]); const totalCost = useCallback(() => { return budgetList.reduce((acc, cur) => acc + cur.cost, 0); }, [budgetList]); 최상위 컴포넌트인 <App> 컴포넌트에서 만든 'budgetList'이라는 state를 useEffect와 useCallback의 의존성 배열에 추가했다.useEffect에서는 해당 state가 변경되면 로컬 스토리지의 budgetList를 최근의 리스트로 변경한다.이렇게 하면 일일이 setBudgetList가 호출되는 곳마다 함수를 사용하지 않아도 된다.다음은 예산의 총 금액을 반환하는 함수가 리스트가 변경될 때마다 실행되도록 useCallback으로 감싸고 의존성 배열에 state를 추가했다.// Form.jsx const budgetNameRef = useRef(); const [budgetName, setBudgetName] = useState(""); const [budgetCost, setBudgetCost] = useState(0); useEffect(() => { if (isEdit) { setBudgetName(budget.name); setBudgetCost(budget.cost); budgetNameRef.current.focus(); } }, [isEdit]); <Form> 컴포넌트에서는 useEffect에 'isEdit'이라는 state를 의존성 배열에 추가했다.사용자가 예산을 수정하기 위해 list의 Edit 버튼을 클릭하면 해당 budget의 name과 cost를 최근 state로 불러오고, useRef를 이용해 name을 입력하는 <input> 요소에 focus 상태가 되도록 했다.state 를 전역 변수처럼(?) 사용해보기// App.jsx const [currentBudget, setCurrentBudget] = useState({ isEdit: false, budget: {}, }); // List.jsx const handleEdit = () => { setCurrentBudget({ isEdit: true, budget: list, }); setHandleStatus({ type: "edit", message: "Editing..." }); }; // Form.jsx const handleBudgetSubmit = (e) => { const newBudget = { id: Date.now(), name: budgetName, cost: budgetCost, }; // isEdit의 값에 따라 새로 추가할지 수정할지 결정 if (isEdit) { setBudgetList((prevBudgetList) => { const newBudgetLists = [...prevBudgetList]; const index = newBudgetLists.findIndex(({ id }) => id === budget.id); newBudgetLists[index] = newBudget; return newBudgetLists; }); setCurrentBudget({ isEdit: false, budget: {} }); setHandleStatus({ type: "submit", message: "Edit Success!" }); } else { setBudgetList((prevBudgetLists) => [...prevBudgetLists, newBudget]); setHandleStatus({ type: "submit", message: "Submit Success!" }); } // submit 종료 시 input의 데이터를 초깃값으로 설정 setBudgetName(""); setBudgetCost(0); }; 배웠던 To Do 앱은 List의 Edit 버튼을 클릭했을 때 해당 List의 요소를 input 요소로 변경시키고 수정을 했다.하지만 과제는 클릭을 했을 때 List의 요소를 변경시키는 게 아니라 Form의 input에 해당 예산의 데이터를 전달해야 했다.그래서 마치 전역 변수처럼 사용할 'currentBudget'이라는 state를 생성하고 'isEdit'이라는 boolean 값과 수정할 예산의 데이터를 담을 'budget'이라는 값을 설정했다.'isEdit'의 상태 값이 true일 때 수정하기와 삭제하기 <button> 요소를 disabled로 변경한다.또한 submit 함수는 새로운 입력 값을 budgetList에 추가하지 않고 해당 예산의 index를 찾아 수정하고 리스트를 변경한다.이렇게 하니 onSubmit과 onEdit 처럼 비슷한 기능을 하는 함수를 여러 개 만들지 않아도 되었다. ⚠ setTimeout 렌더링const { type, message } = handleStatus; const handleStyle = useCallback(() => { if (type === "edit") { return "text-gray-500 block"; } else if (type === "none") { return "hidden"; } else { // 2초 뒤에 실행 --> App - Form - Status 1번 더 렌더링 setTimeout(() => { setHandleStatus({ type: "none", message: "" }); }, 2000); if (type === "submit") { return "text-green-400 block"; } else { return "text-red-400 block"; } } }, [type]); 추가, 삭제, 수정의 완료 및 진행 중 상태를 보여주는 <Status> 컴포넌트를 만들었다.App에서 만든 'handleStatus'라는 state를 전달하고 메세지가 나타난 뒤에 사라지게 만들고 싶어서 setTimeout() 메서드를 이용해 2초 뒤에 상태를 초기화했다.하지만 이 상태가 App과 Form 컴포넌트에서 참고하다 보니 나타나고 사라질 때마다 렌더링이 발생했다.CSS의 opacity로 처리하기엔 state의 값을 변경해야 했기에 알맞는 방법은 아니라 생각했다.뭔가 <Status> 컴포넌트 내부에서만 렌더링이 일어나게 하고 싶었는데 아직 다른 방법을 찾지 못했다.😢😢😢 회고다른 컴포넌트의 클릭 이벤트로 변경된 state를 이용하는 부분이 생각보다 오래 걸렸다.처음엔 콜백 함수처럼 App 컴포넌트에서 함수 만들고 prop으로 넘겨봤지만 List와 Form은 종속적인 관계가 아니라 힘들었다. 😢그래서 생각해낸 게 state를 이용해서 상태의 변경을 이벤트처럼 사용하는 것이었다.pub-sub 혹은 observer 패턴 같다는 생각도 했지만, 이렇게 최상위에서 선언한 state가 이곳저곳 돌아다니는 게 좋은 방법은 아닐 것 같다는 생각이 들었다.규모가 커지면 렌더링 관리도 힘들고 props를 쫓아다녀야 하기 때문이다.이래서 상태 관리 라이브러리가 나왔나 보다. 🤔 

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

이양구

[인프런 워밍업 클럽 FE 0기] 미션4-2 - GitHubFinder 앱

🔍 github-finder-app GitHub 🔍 github-finder-app 개요인프런 워밍업 클럽 FE 0기의 네 번째 미션인 'GitHubFinder 앱' 입니다.따라하며 배우는 자바스크립트 섹션 5(OOP), 섹션 6(비동기) 목표Fetch API 를 이용해 깃허브 유저 목록 불러오기Closure 를 이용해 Debounce Function 만들기 구현Fetch API 를 이용해 깃허브 유저 목록 불러오기async function loadUser(input) { prevInputValue = input; try { // const response = await fetch('./src/javascript/user.json'); const response = await fetch(`${url}/${input}`); if (!response.ok) { throw new Error('Failed to fetch user json'); } const json = await response.json(); setUserAvatar(json); setUserInfo(json); await loadUserRepos(json); } catch (error) { console.error(error); } }fetch() 메서드의 응답은 HTTP 응답 전체를 나타내는 'response' 객체를 반환한다.response의 ok 속성은 응답의 성공 여부를 불리언 값으로 가지고 있다.따라서 응답이 성공이 아닐 경우 오류 객체(new Error())를 반환하고 catch 문으로 Promise의 오류를 처리한다.응답에 성공한 response 객체를 JSON으로 사용하기 위해선 json() 메서드를 이용해 파싱해야 한다. Closure 를 이용해 Debounce Function 만들기// debounce debounceInput.addEventListener('input', debounce(loadUser, 1000)); // debounceInput.addEventListener('input', e => callback(e)); function debounce(callback, delay = 0) { // timer는 부모 함수에서 선언된 지역 변수 let timer = null; return (arg) => { // 여기서 arg는 input event if (timer) { // 이미 타이머가 있는데 또 실행되면 타이머 삭제 clearTimeout(timer); } // 변수 timer는 부모 함수에서 선언되었지만 내부 함수에서 사용(클로저) timer = setTimeout(() => { callback(arg.target.value); }, delay); }; }<input> 요소의 'input' 이벤트는 요소의 value가 변경될 때마다 발생한다.만약 사용자가 입력할 때마다 서버에 데이터를 요청한다면 서버의 부하가 커지기 때문에 좋은 방법은 아니다.이럴 때 사용자의 입력이 끝난 뒤 마지막 value를 이용해 서버로 요청하는 게 효율적인 방법이라 할 수 있다.함수의 실행 요청이 반복될 때 마지막 요청만으로 실행하는 걸 '디바운싱(debouncing)'이라고 부른다.debounce 함수는 인자로 실행할 함수를 받고 자식 함수를 반환한다.부모 함수인 debounce 함수에서 선언한 변수(timer)를 자식 함수에서 사용할 수 있는 클로저(Closure)를 이용해 자식 함수의 setTimeout() 메서드의 반환 값인 'timeoutID'를 할당한다.변수 'timer'에 할당한 timeoutID를 이용해 setTimeout() 메서드의 지연 시간(delay)이 종료되기 전에 요청이 들어왔다면 이전에 생성한 타이머를 clearTimeout() 메서드를 이용해 종료하고 다시 타이머를 할당한다. 이렇게 delay로 설정한 시간 이내에 사용자의 입력이 없을 경우 API 요청 함수를 실행하게 된다. 반복적인 함수의 실행을 다루는 방법으로 디바운싱(debouncing)와 쓰로틀링(throttling)이 있다.여러 변수를 고려해 'lodash' 라이브러리의 debounce를 많이 사용한다. 회고이번 미션은 debounce가 반환하는 자식 함수의 인자(argument)가 어떤 타입인지 알기 때문에 callback 함수에 전달하는 인자를 수정해서 미숙한 debounce 함수라고 볼 수 있다.늘 라이브러리를 통해 사용하던 함수를 만들려고 하니 모르는 것도 많고, 고려해야 할 부분이 많다는 걸 알게 됐다.자바스크립트의 기초를 잘 알아야 이런 라이브러리 메서드의 원리를 이해하기도 쉽고, 커스텀하기에 수월한 것 같다.(외의로 GitHub의 API 요청이 API key 없이도 되어서 신기했고, 그 덕에 조금은 수월했다. 아주 조금... 😵) DemoRecord by ScreenToGif

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

이양구

[인프런 워밍업 클럽 FE 0기] 미션1 - 음식 메뉴 앱

🍝 food-recipe-app API from TheMealDBGitHub food-recipe-app 개요인프런 워밍업 클럽 FE 0기의 첫 번째 미션인 '음식 메뉴 앱' 만들기입니다.따라하며 배우는 자바스크립트의 섹션 1~3(자바스크립트 기초, Window 객체 및 DOM, Event)를 보고 자바스크립트의 DOM 요소를 조작하는 데 중점을 두었습니다.음식 데이터는 TheMealDB의 API를 이용했습니다. 사용한 API가 '음식 레시피'라서 이름을 변경했습니다. 목표문서 객체 모델(The Document Object Model, 이하 DOM)의 메소드(methods)를 이용해 요소(element)에 접근하고 생성하고 교체하기이벤트 리스너(Event Listener) 메소드를 이용해 요소에 이벤트를 등록하고 이벤트 객체 이용하기메뉴 데이터를 Fetch API를 사용해 불러오기 구현이벤트 위임(Event Delegation)을 이용한 이벤트 생성/* <nav id="food-navigation"> <div class="food-navigation-item"> <button id="Beef"> <figure> <img src="https://www.themealdb.com/images/category/beef.png"> <figcaption> Beef </figcaption> </figure> </button> </div> // ... </nav> */ // Not Event Delegation foodNavigation.querySelectorAll('button').forEach((button) => { button.addEventListener('click', async () => { const targetId = button.id; await setFoodList(targetId); }); }); // Event Delegation foodNavigation.addEventListener('click', async (event) => { const targetElement = event.target; // closest() 메서드는 주어진 CSS 선택자와 일치하는 요소를 찾을 때까지, // 자기 자신을 포함해 위쪽(부모 방향, 문서 루트까지)으로 문서 트리를 순회합니다. const targetDiv = targetElement.closest('.food-navigation-item'); if (!targetDiv) { return; } const targetButton = targetDiv.querySelector('button'); const targetId = targetButton.id; await setFoodList(targetId); });이벤트 위임이란 '상위 요소에서 하위 요소의 이벤트를 제어하는 것'을 의미합니다.이벤트를 위임하는 이유이벤트를 하나의 핸들러로 처리함으로써 메모리 사용량을 줄이고 성능을 향상시킬 수 있다.새로운 요소가 추가되거나 제거되는 경우 이벤트 리스너는 상위 요소에 연결되어 있어 재연결의 필요성이 줄어든다.저는 nav 태그에 이벤트를 등록하고 closest 메서드를 이용해 버튼의 id를 찾는 방법을 사용했습니다. 하위 요소를 제거하고 생성한 요소를 추가하기/* <div id="food-list"> <div class="food-list-item"> <figure> <img src="img src" /> </figure> <div class="food-list-item-desc"> <p>food name</p> <hr /> <div> food recipe </div> </div> </div> </div> */ const foodList = await getFoodList(strCategory); const foodListElement = document.getElementById('food-list'); const foodListItem = document.querySelectorAll('.food-list-item'); foodListItem.forEach((item) => item.remove()); // foodListElement.innerHTML = ''; foodList.map(async (food) => { // ... const foodElement = getFoodElement( idMeal, strMeal, strMealThumb, strInstructions ); foodListElement.appendChild(foodElement); });배웠던 removeChild()와 replaceChild() 메서드를 이용하고자 했으나...'만약 해당 카테고리의 음식 리스트의 개수가 다르다면 어떻게 하지?'라는 생각에 한번에 제거하기로 결정했습니다.처음엔 innerHTML을 이용해 하위 코드를 공백으로 만들었지만, 뭔가 이건 너무 이상하다는 생각(요소의 참조나 연결 같은 게 깨지진 않을까)이 들어 찾아보았습니다.stack overflow의 Remove child nodes (or elements) or set innerHTML=""?라는 글에서는 innerHTML은 하위 요소의 이벤트 핸들러가 완전히 제거되지 않을 수도 있다고 한다.또한 Why InnerHTML Is a Bad Idea and How to Avoid It?에서는 innerHTML이 보안상 좋지 않다는 점을 말하고 있다. Stack Overflow의 글을 자세히 읽어 보니 다음과 같은 글이 있었다.What is the best way to empty a node in JavaScript그리고 MDN 문서에도 이렇게 소개하고 있다.replaceChildren() provides a very convenient mechanism for emptying a node of all its children. You call it on the parent node without any argument specified:즉 replaceChildren()메서드를 빈 인자로 실행하면 하위 자식 노드를 모두 지워준다는 것...!😅 회고빈 폴더를 놓고 코드를 작성해본 게 너무 오랜만인 것 같다.자료를 찾기 귀찮다는 마음과 첫 미션이니까 API를 써볼까 하며 자만했던 순간도 있었다.미션의 목적보다 어느새 다른 부분을 신경 쓰느라 배보다 배꼽이 점점 커지는 것 같았다.딸랑 script 태그 한 줄 작성하고 js 파일을 제대로 못 불러와서 몇 시간을 해결 방법을 찾아서 해매기도 했다.😭이벤트 위임 코드를 작성할 때 이은재 님의 시나브로 자바스크립트에서 배웠던 부분을 참고했다.음식 레시피를 불러올 때 요소를 지우고 불러와서 그런지 해당 부분이 사라지고 나타나서 페이지가 늘었다 줄었다 하는 게 눈에 띈다.이래서 가상 돔을 쓰는걸까? 아니면 태그의 속성을 하나하나 수정하면 되는걸까?일단 진도를 따라잡고 배워서 발전시켜야겠다. DemoRecord by ScreenToGif

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

bananachacha

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

강의 출처https://inf.run/XKQg 1주차 학습 내용 요약 Day 1: 개발 환경 설정 및 네트워크 기초스프링 프로젝트를 만들고, 웹에서 데이터를 주고 받는 방법에 대해 이해했습니다.API를 만들기 전에 API 설계를 어떻게 하는지 배우고, 그것을 기반으로 스프링의 어노테이션을 사용하여 실제 API를 개발했습니다. Day 2: 첫 HTTP API 개발클라이언트로부터 JSON 형식의 데이터를 받아와서 처리하는지에 대한 기본 코드를 작성하면서, 실제 도서 관리 애플리케이션을 만들어보았습니다. Day 3: 기본적인 데이터베이스 사용법디스크와 메모리의 차이를 이해하고, 간단한 엑셀로 비유를 통해 MySQL이라는 데이터베이스를 어떻게 다루는지 학습했고, MySQL을 사용하여 데이터베이스를 조작하는 기초적인 CRUD 작업을 익혔습니다.또한, 스프링 서버에서 실제로 데이터베이스에 접근하도록 설정 파일을 작성하고 Jdbctemplate을 이용한 데이터베이스 연동을 학습했습니다. Day 4: 데이터베이스를 사용해 만드는 API요청 값의 검증과 예외 처리에 대한 로직을 작성하여 안정적으로 업데이트와 삭제 API를 개발했고, 스프링 서버가 데이터를 저장, 조회, 업데이트, 삭제할 수 있는 모든 기능을 구현했습니다. Day 5: 클린 코드의 개념과 첫 리팩토링클린 코드의 중요성과 코드를 깔끔하게 유지하기 위해 Layered Architecture를 도입하여 Controller, Service, Repository로 3단 분리하여 코드를 리팩토링하는 방법에 대해 배웠습니다.이를 통해 코드의 가독성과 유지보수성을 향상시키는 방법을 알게 되었습니다.  1주차 미션 Day1 어노테이션을 사용하는 이유(효과)는 무엇일까?나만의 어노테이션은 어떻게 만들 수 있을까? 어노테이션에 대해 학습하여 해당 질문에 대한 답변을 스스로 작성해볼 수 있는 미션이었습니다.평소에 스프링에서 어노테이션을 사용할 때 그냥 사용하면 되는구나 정도로만 알고 있었는데, 어노테이션의 기본 문법부터 커스텀 어노테이션까지 학습하면서 어노테이션의 역할과 장점을 이해할 수 있었습니다.보일러 플레이트 코드를 줄여주는 이점과 애플리케이션의 고유한 요구사항에 맞게 커스텀한 어노테이션의 사용이 개발 생산성에 도움을 줄 수 있다는 점도 알게 되었습니다.이제는 개념들을 단순히 사용하는 것이 아니라, 그 뒤에 숨은 이유와 장점을 고려하여 개발에 적용할 수 있게 되었습니다. 블로그https://velog.io/@awesomehill/%EC%9D%B8%ED%94%84%EB%9F%B0-%EC%9B%8C%EB%B0%8D%EC%97%85-%ED%81%B4%EB%9F%BD-0%EA%B8%B0-%EB%B0%B1%EC%97%94%EB%93%9C-%EC%B2%AB-%EB%B2%88%EC%A7%B8-%EA%B3%BC%EC%A0%9C  Day2 GET API와 POST API 개발하기 문제에 제시된 API Spec을 보고 API를 개발하는 미션이었습니다.먼저, GET API를 개발할 때 쿼리 파라미터의 처리 방법에 대해 고민하였습니다.쿼리 파라미터의 개수가 한 개인 경우에는 변수에 바로 바인딩하는 방식을 선택하였고, 여러 개의 쿼리 파라미터가 필요한 경우에는 Dto 클래스를 활용하여 요청을 매핑하는 방식을 선택했습니다.POST API를 개발할 때에는 Request Body로 전송되는 JSON 데이터를 받아오기 위해 Request Dto를 만들고, 이를 통해 데이터를 매핑하는 코드를 작성했습니다.이때, Java 14에서 추가된 record를 활용하여 Dto를 불변 객체로 만들어 코드의 안정성을 높였습니다.또한, Java 8 이후로 도입된 LocalDate 클래스를 사용하여 Date 클래스와의 차이점과 날짜 데이터를 처리하는 방법을 익힐 수 있었습니다. 블로그https://velog.io/@awesomehill/%EC%9D%B8%ED%94%84%EB%9F%B0-%EC%9B%8C%EB%B0%8D%EC%97%85-%ED%81%B4%EB%9F%BD-0%EA%B8%B0-%EB%B0%B1%EC%97%94%EB%93%9C-%EB%91%90-%EB%B2%88%EC%A7%B8-%EA%B3%BC%EC%A0%9C  Day3 자바의 람다식은 왜 등장했을까?람다식과 익명 클래스는 어떤 관계가 있을까? - 람다식의 문법은 어떻게 될까? 람다식이 등장한 이유와 익명 클래스와의 관계에 대해 알아보면서 해당 질문에 대한 답변을 생각할 수 있는 미션이었습니다.기존에는 익명 클래스를 사용하여 함수형 인터페이스를 구현하는데 많은 코드를 필요로 했으며, 이는 작은 작업을 수행하는 경우에도 코드의 양이 불필요하게 많아지는 문제를 야기했습니다.하지만, 람다식의 등장으로 이러한 불편함을 해소하고, 간단한 표현으로 함수형 인터페이스를 구현할 수 있게 되었습니다.람다식이 어떻게 코드를 간결하게 만들어주는지를 실제 코드를 통해 경험하면서 개념을 익힐 수 있었습니다. 블로그https://velog.io/@awesomehill/%EC%9D%B8%ED%94%84%EB%9F%B0-%EC%9B%8C%EB%B0%8D%EC%97%85-%ED%81%B4%EB%9F%BD-0%EA%B8%B0-%EB%B0%B1%EC%97%94%EB%93%9C-%EC%84%B8-%EB%B2%88%EC%A7%B8-%EA%B3%BC%EC%A0%9C  Day4 데이터베이스를 사용해 API 개발하기 JdbcTemplate을 활용하여 데이터베이스와 상호 작용하는 방법에 대해 깊게 학습했습니다.필요한 테이블을 생성하고 JdbcTemplate을 사용하여 SQL 쿼리를 실행하여 데이터를 다루는 방법을 익혔습니다. 예제를 따라가면서 HTTP 요청에 따라 데이터베이스에 새로운 데이터를 추가하거나 업데이트하는 코드를 작성하면서 JdbcTemplate의 실제 활용도를 체감할 수 있었습니다.테이블에 is_sold 칼럼을 추가하여 데이터의 판매 여부를 명시함으로써 데이터베이스의 일관성과 코드의 가독성을 향상할 수 있었습니다.그리고 문제 3에서 SUM이나 GROUP BY 등의 집계 함수에 대한 정확한 이해가 필요하다고 느껴, 틈틈히 SQL 공부를 병행해야 겠다는 생각이 들었습니다. 블로그https://velog.io/@awesomehill/%EC%9D%B8%ED%94%84%EB%9F%B0-%EC%9B%8C%EB%B0%8D%EC%97%85-%ED%81%B4%EB%9F%BD-0%EA%B8%B0-%EB%B0%B1%EC%97%94%EB%93%9C-%EB%84%A4-%EB%B2%88%EC%A7%B8-%EA%B3%BC%EC%A0%9C  Day5 클린 코드 개념을 적용하여 코드 리팩토링하기 클린 코드 작성하는 방법에 대해 찾아보고 과제에 제시된 코드를 리팩토링하는 미션이었습니다.코드를 입력, 실행, 출력 단계로 명확히 분리하여 가독성을 향상하여 Main 클래스에서 코드의 흐름을 쉽게 이해할 수 있도록 설계했습니다.코드를 읽는 개발자가 다른 개발자의 의도를 이해하는 데 도움이 될 수 있도록 메소드명과 변수명을 코드의 목적과 기능을 명확히 드러낼 수 있도록 작성했습니다.코드를 작게 만들어 가독성을 높이고, 재사용성을 증가시키기 위해 클래스와 메소드는 하나의 책임만을 갖도록 설계했습니다.상수를 활용하여 반복되는 문자열을 상수로 정의하여 코드의 일관성을 유지하고 오타를 방지했습니다.주사위를 나타내는 클래스에서 OOP 개념을 도입하여 유연성을 높이고, 나중에 주사위 범위를 변경해도 코드의 수정이 용이하도록 구조화했습니다.이번 미션을 통해 클린 코드 작성의 중요성과 가독성의 영향력을 체감할 수 있었습니다.코드를 작성하는 것뿐만 아니라, 코드를 읽는 것이 개발에 있어서 중요한 부분임을 깨닫게 되었습니다.  블로그https://velog.io/@awesomehill/%EC%9D%B8%ED%94%84%EB%9F%B0-%EC%9B%8C%EB%B0%8D%EC%97%85-%ED%81%B4%EB%9F%BD-0%EA%B8%B0-%EB%B0%B1%EC%97%94%EB%93%9C-%EB%8B%A4%EC%84%AF-%EB%B2%88%EC%A7%B8-%EA%B3%BC%EC%A0%9C  1주차 회고 프로젝트를 진행하면서 스프링 JPA에 대한 학습을 병행했는데, 조금은 힘든 도전이었지만, 그만큼 성취감과 만족도도 크게 느낄 수 있었습니다.또한, 인프런 스터디를 통해 다양한 주제에 대한 학습과 토론을 할 수 있어 지식의 폭을 넓히는 데에 도움이 되었습니다.여러 일을 병행하면서 힘들기도 했지만, 이러한 경험이 성장의 발판이 될 것이라고 생각합니다.앞으로도 끝까지 완주할 수 있도록 노력하겠습니다:)

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

유원준

[ 인프런 워밍업 클럽(백엔드, 0기)] - 0일 차 오리엔테이션 회고!

안녕하세요! 이번 인프런 워밍업 클럽 백엔드 스터디 0기에 참여하게 되었습니다.기존 Java, Spring, JPA로 백엔드를 공부했고, 그동안 배웠던 내용을 체화시켜야 할 기회가 필요했습니다.학습만 하고 체화시키지 않는다면 절대 본인의 지식이 될 수 없다고 생각하고 있기 때문입니다. 만약 학습한 내용을 바로 머릿속에 오래 남겨둘 수만 있다면 큰 행운인 것 같습니다. 하지만 저도 사람이기에 단순히 눈으로 보기만 한 내용은 기억에 오래 남지 못하는 것 같습니다. 저랑 비슷하신 분이 계실진 모르겠지만 배운 내용, 실습한 내용들을 본인의 GitHub나 개인 블로그에 잘 정리해 놓으면 상당히 기억에 오래 남고, 필요한 부분을 다시 찾아볼 수 있다는 점 때문에 저는 기록하는 것을 항상 좋아합니다! 제 루틴대로 배웠던 학습 내용들을 정리하고, 과제, 프로젝트 수행 부분 또한 별도로 블로그, GitHub에 정리할 예정입니다. 열정 가득한 코치님, 참가자분들과 함께 공부할 수 있게 되어 기쁜 마음입니다!백엔드 0기로 합류하시게 된 분들 모두 같이 파이팅했으면 좋겠습니다 😀 감사합니다 😀 

백엔드인프런워밍업백엔드스프링발자국0기화이팅!

강호연

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

1. 강의 관련새로 배운 내용console 객체 console.log 외에 .table, .error, .warn, .time, .clear 등 다양한 메소드가 있다. 변수 선언 let const는 호이스팅 되도 초기화가 일어나지 않아서 TDZ에서 호출시 undefined가 아닌 에러가 뜸객체 타입  콜 스텍에 값이 그대로 저장되는 원시타입과 달리, 객체 타입은 주소만 저장 실제 값은 메모리 힙에 저장이 된다 forEach for 문에서 루프제어를 위한 break, continue를 사용할 수가 없기에 return 등의 꼼수를 써야한다. 웬만하면 배열의 모든 요소를 순회해야할 때만 쓰는 게 좋은 듯 window 객체 원래 스크롤 관련 메소드들만 알고 있었는데, location, history, cliboard 등의 유용한 메소드들이 있다. innerHTML textContent, innerText html 내부 값을 바꿀 때 무지성으로 innerHTML만 써왔는데 비용이 높다고 한다. 내부 텍스트만 수정이 되는 경우에는 text 관련 메소드를 사용하는 것이 좋다.iterator와 generator다시 공부해야함. 뭔소린지 모르겠음회고다행이 미리 알고 있던 내용이 대부분이고, 강의도 미리 들어둬서 진도에는 맞출 수 있었지만 리액트 파트는 진도를 어떻게 따라가야할지 조금 막막하다 지식은 가장 자신만만할 때가 갓 기본을 뗐을 때인 것 같다. 기본 강의 완강하고 뭐든 만들 수 있을 것 같던 때에서 3배는 더 많은 지식을 습득했지만 여전히 JS는 자신이 없다. 리액트 파트 시작 전에 조금 더 심도있게 학습을 해야할 것 같다. 2. 미션 관련Click! 미션 GitHub 주소각 프로젝트별 상세는 각 프로젝트 readme 파일에 서술해 뒀으니 간단하게 언급하고 넘어가려한다. 미션 1 음식 메뉴앱로직 플로우메뉴 항목들이 속한 ul에 click eventListener를 부착하여 개개의 li가 아닌 ul에서 이벤트를 처리한다.클릭된 요소의 name 속성(카테고리 정보)와 data.js에서 갖고온 데이터를 renderMenu 함수에 전달한다.renderMenu함수는 data에서 선택된 메뉴에 해당하는 데이터를 선별 후 화면에 그린다.  회고하는 내내 리액트면 금방하는데 라는 생각을 했던 것 같다. 그래서 리액트 처럼 랜더로직과 데이터를 분리하여 만드는 것을 목표로 했다. 미션 2 가위바위보로직 플로우 시작하기를 누르면 게임화면으로 넘어간다. 컴퓨터는 가위 바위 보 중 랜덤으로 낸다. 가위 바위 보 중 뭐든 냈을 경우에 승패에 따라 스코어를 갱신하고 결과를 보여준다. 한 세트가 끝나면 종합 스코어를 표시하고, 다시하기 버튼으로 다시할 수 있도록 한다. 회고 만들면서 시행착오를 가장 많이 한 프로젝트이다. setTimeout과 setInterval을 원없이 써봤다. 기본 구현 사항이 너무 시시하고 디자인도 좀 아쉬워서, 두 부분을 좀 더 확장해봤다. 미션을 보고 짱꼔뽀가 생각나서 디자인은 거기서 레퍼런스를 얻어 만들었고, 로직은 '안내면 진거'를 추가 했다. 로직 자체는 간단한데 interval을 어디서 clear할지를 고민해야했다. 우측의 카운트 다운과 가운데 컴퓨터 픽이 바뀌는 두 부분이 서로 다른 interval로 돌아가기 떄문이다. 이 때문에 두 interval을 동시에 관리할 수 있는 패턴을 고안하여 설계했다.안 내면 진거 가위바위보 미션 3 퀴즈 앱로직 플로우 문제 데이터를 html에 바인딩고룬 선택지에 따라 화면을 분기정답인 경우오답인 경우 다음 버튼을 눌러 다음 문제로 ( 갖고올 데이터 위치++)회고 미션 영상을 여러번 돌려봤지만 도무지 뭘 만들어야하는 건지 알 수 없었다. 최소 구현 스펙을 서술해주면 좋겠다. 그래서 로직 자체보단 데이터처리나 디자인 처리에 주안점을 두고 미션을 수행했다. 퀴즈 데이터는 chatGPT에게 프론트엔드 퀴즈 10개를 내달라고 부탁해서 데이터를 갖고 왔다. 그러자 문제 데이터를 txt로 주길래 연습할 겸 fs로 데이터를 갖고와서 데이터를 파싱하는 함수를 작성했다(data.js),함수를 작성하던 중 문득 'json형식으로 퀴즈 데이터 만들어줘' 라고 질문하면 되지 않을까 싶어서 해보니 정말 됐다....그러나 연습이니 변수명만 참고하고 직접 해보았다. 또, 알기만 하고 써보진 않았던 ::after 선택자를 활용해서 정오답 처리(o, x)를 진행했다.번외로 가장 디테일을 많이 챙긴 미션이다.배경색은 안과의사가 추천하는 눈이 피로하지 않은 색이다. 퀴즈 푸는 데 머리만 아파야지 눈이 아프면 안 되지, 안 그런가?문제 부분의 텍스트는 수능에서 쓰는 중명조체이고 css font-face를 통해 갖고 왔다.정답 표시 파트에서 선택지에 hover시 노란색으로 변하는 css는 정오답 선택지에 대해서는 무시된다. (정오답 선택지에 !import 붙임) 미션 4 리스트 앱 리액트 기본예제에서 지겹도록 해본 todo앱이다. MVC 패턴을 사용해보려 노력했다. 또 no-CSS 프로젝트로 html만을 이용해서 페이지를 구성했다. 로직 플로우 리스트 추가 및 삭제데이터 수정 (MODEL)데이터 추가 : bookData 배열에 데이터를 추가한다.데이터 삭제 : bookData 배열에서 데이터를 삭제한다.랜더링 (CONTROL) : 수정된 데이터를 받아와서 template(VIEW)에 데이터를 바인딩하여 랜더링알림 : 데이터 수정 후 성공 알림을 띄움 (dialog //화면 중간에 팝업창 뜸)회고 유저 플로우를 생각하여 입력 편의 개선에 신경을 썼다. 별 건 아니지만 꽤 편하다.페이지 최초 진입시 제목 input에 autofocus되도록 속성을 추가함책을 등록 후 제목 input을 비우고, 제목 input에 focus가 되도록 함  생각보다 다양한 HTML 테그가 있다는 것을 알게된 프로젝트이다. dialog, fieldset, legend, table 등 평소 잘 안 써본 테그들을 적극 기용하여 마크업에 힘을 썼다. 특히 dialog 테그를 조작하는 과정에서 js로 html 속성을 다루는 방법을 심도 있게 학습할 수 있던 것 같다. 미션 5 GitHub Finder 앱fetch를 통해 github api를 활용해보는 것이 목표인 프로젝트이다. Next.js에서 지겹게 해본 부분이라 쉽게 해결할 수 있었다. 로직 플로우  검색창 input에 input 이벤트리스너를 부착하여 검색어를 받는다.input의 값이 바뀔 때마다 fetch 함수를 통해 데이터를 불러온다.가져온 데이터를 바인딩한다. (단, 데이터가 없는 경우 (response.message == 'Not Found') 에는 유저 데이터를 표시하지 않는다.). 회고 github OAuth 인증하고 토근을 같이 보내지 않으면 시간당 20건의 요청만 거부되지 않고 할 수 있다. 이 부분을 찾아보고 해결해도 잘 되지 않았다.. mock 데이터를 활용할까 했지만 데이터를 만드는 것이 더 시간이 걸릴 것 같아서 요청을 최소화하면서 날마다 조금씩 작업을 했다. 다행이 어렵진 않아서 금방 끝났지만 다음에는 캐싱을 하든 뭘 하든 해결책을 강구해야할 것 같다. 미션 6 비밀번호 생성 앱로직 플로우  선택된 옵션 항목에 따라 문자풀을 생성한다.생성할 비밀번호 길이와 문자풀을 통해 랜덤 비밀번호를 생생한다.생성된 비밀번호를 결과창에 표시한다.  회고 chatGPT에게 html, css를 맡겼는데 checkbox input과 label을 이용하여 꽤나 세련된 옵션창을 만들어줘서 놀랐다. 물론 fieldset과 legend는 직접 수정했다. 난이도가 제일 쉽고 코드도 가장 짧은 프로젝트였다. 영문 대소문자를 베이스로 하고, 특수문자는 홈페이지마다 비밀번호 양식이 다르니 유저에게 입력 받는 방식으로 만드는 게 더 좋지 않았을까 싶다.  문자풀을 생성하는 과정에서 fromCharCode라는 String의 메소드를 찾았는데 직접 [a,b,c...] 하지 않아도 되서 편했다.let pool = []; //영문 대문자를 풀에 추가 if (qs("#include-uppercase").checked) { const uppers = Array.from({ length: 26 }, (_, idx) => String.fromCharCode(idx + 65) ); pool = pool.concat(uppers); ... } 프로젝트 관련 회고기능 구현은 다 되지만 이게 잘 짜여진 코드인지 아닌지를 스스로 판단할 힘이 다소 부족한 것 같다. 다른 사람들 코드를 참고하면서 좋은 부분을 흡수할 수 있도록 해야겠다. 그리고 미션 7. 타이핑 테스트는 가장 재밌는 미션인데 시간이 너무 부족해서 무사히 마무리할 수 있을지 모르겠다.

프론트엔드워밍업클럽발자국

유초

인프런 워밍업 클럽 스터디 1기 FE | 1주차 발자국

강의 수강 프론트엔드 부트캠프를 수강했었지만 그곳에서 내가 맡은 역할은 모두 백엔드였기 때문에 이번 인프런 스터디에서는 프론트엔드에 대해 많은 것을 배워가고 싶었다 ! 사실 자바스크립트와 리액트 모두 많이 까먹은 상태여서 이론 강의에 많이 치중해 있었다. 내가 부족한 부분들은 다시 듣고 하느라 진도도 많이 느린 상태여서 일단 진도표에 맞춰 얼른 진도를 맞춰야겠다는 생각이 많이 든다 ! 자바스크립트 기초, DOM , Event, 자바스크립트 중급, 비동기, Iterator, Generator 부분을 공부하면서 부족한 부분은 내가 몇 개월 전에 공부하면서 봤던 모던 자바스크립트 Deep Dive 책을 참고하였다. 다음주부터는 하루하루의 기록을 제대로 해놓아서 발자국을 제대로 쓸 수 있도록 노력해야겠다. 미션 이론강의에 시간을 많이 쏟다보니 미션을 제대로 수행하지 못하였다. 하다가 막히고 추가기능도 제대로 구현을 하지 못해서 미션 수행률이 더 저조해진 것 같다. 빨리 자바스크립트의 미션 3개를 완료하고 진도를 따라가야 할 것 같다.  미션에 대한 발자국도 진도율에 맞춰 올리는 것이 목표이다. 리액트를 들어가기 전에 자바스크립트를 제대로 파악하고 과제도 끝마친 상태로 들어가야 리액트 과제를 하는데도 지장이 없을 것 같다 ! 회고다음주에는 스터디 발표도 진행하게 되는데, 발표자료도 열심히 준비해서 나와 팀원들에게 도움이 되었으면 좋겠고, 내가 부족한만큼 다른 분들에게서도 많은 것을 보고 배우고 싶다 ! 그리고 강의도 미션도 빨리 진도율을 맞춰서 진행하도록 해야겠다 ! 다음 발자국은 더 나은 발자국이 되길 !

프론트엔드프론트엔드워밍업클럽FE1기발자국

역직렬화

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

<자바와 스프링 부트로 생애 최초 서버 만들기, 누구나 쉽게 개발부터 배포까지! [서버 개발 올인원 패키지]> 생애처음 스프링부트로 API 짜기 도전하면서 자율스터디를 해본 나란 사람, 선택과 집중이 필요한시점이기에 더욱 의미있는 스터디!현재 회사에 프로젝트가 너무 고되기도 하고 강의를 들을 때 한번에 집중 할수 있는 시간이 부족하여 주차별로 드문드문 들은거 같아서 아쉬운 마음이 크지만 내가 성장할 수 있는 시간이 점차 느껴지는 것 같아서 1주차 발자국을 남깁니다.  개발자들 특유의 API 짰어요 라는 말을 나도 이제 드디어 할 수 있다니!덧셈 API 짜는것부터 너무 이해하기 쉽고 차근차근 알려주셔서 얼마나 도움이 됐는지 모른다.  내가 강의를 통해 얻고 배운 인사이트 요약드디어 본격적인 덧셈 API 짜기@Restcontroller 진입점@Getmapping api 지정Get 메소드 사용, path는 /add 로 지정서버를 동작시키고 클라이언트가 get으로 보내면 public int함수가 호출된다@RequsetParam 주어지는 쿼리를 함수 파라미터에 넣음같은이름의 쿼리값이 arg로 들어오게 됨1) HTTP 호출 - RequestParam 방식port가 8080이므로 포스트맨에 8080으로 테스트package com.group.libraryapp.controller.calculator; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController public class CalculatorController { @GetMapping("/add") //Get/add public int addTwoNumbers( @RequestParam int number1, @RequestParam int number2 ){ return number1 + number2; }해당 계산API 짠 자바를 저장하고 톰캣 port 8080을 적용한 URL를 요청보낸다정상 호출 확인 200 서버값1) HTTP 호출 - 객체를 받는 방식DTO를 작성해준다. 컨트롤러랑 동일한 디렉토리로 dto 아래 calculator > request를 만들어준다.package com.group.libraryapp.dto.calculator.request; public class CalculatorAddRequest { private final int number1; private final int number2; public CalculatorAddRequest(int number1, int number2) { this.number1 = number1; this.number2 = number2; } public int getNumber1() { return number1; } public int getNumber2() { return number2; } } requestparam을 사용할때의 controller*객체를 사용할때의 controller서버 재시작다시 http에 요청을 보내면 정상 200k를 확인할 수 있다.특히 강의 중간중간 좋습니다는 열심히 하게 되는 원동력이 되었으며  2) POST API 개발GET API에서 POST로 받기에 필요한 선지식이 JSON이다. body로 데이터를 받는 과정이 필요하다JSON이란객체표기법, 즉 무언가를 표현하기 위한 형식이다!{ "name":"푸바오", "age":3 }JSON 표기는 중괄호가 양끝에 있음속성은 각각 쉼표로 구분JAVA 비유 MAP<Object,Object>JSON value에는 다른 JSON이 올수 있다.소감 및 각오자바의 기본지식이 부족해서 스프링부트개념을 내가 이해할 수 있을까? 고민하면서도 강의를 들으면서 그런 고민이 아예사라지게되었다. 위에도 적었듯이 JSON 뜻이나 곱셈API 를 만드는 방법 하나하나를 알려주시고 직접 실습하게 하여 내것으로 만들수 있는 기회까지 얻을 수 있게 되어 이번기회로 자바도 공부하면서 스프링부트API를 짜고 서버에 배포해보는 귀한 경험을 가지고 가려고한다, 반복적인 것이 제일 중요한것을 잊지말고 꾸준히 내것으로 만들자!

백엔드발자국

crispin

[인프런 워밍업 스터디 클럽 1기_FE] 1주차 회고록 정리

0기에 백엔드 스터디에 이어 1기 프론트엔드 스터디를 신청하고, 참여하였다.신청 이유신청하게된 가장 큰 이유는 자바스크립트 랑 JQuery 를 조금씩 사용했었는데, 사실 자바스크립트 를 제대로 공부했던 적이 없고, 이직하는 곳에서는 백엔드와 프론트엔드가 명확하게 나뉘게 되어 이직하기전 프론트엔드 지식을 조금이라도 학습하면 협업을 할때 도움이 많이 될것 같아 신청하게 되었다.자바스크립트 미션아직 1단계 완료 후 2단계를 진행하고 있다. 생각외로 많이 어려워서 진행 속도가 많이 느리다. 리액트 부분으로 넘어가면 훨씬 어려워진다고 했는데 0기때 왜 프로트엔드에서 우수 러너가 적게 나왔는지 새삼 느끼고 있다. 1주차 느낀점하필 이직 시기와 겹치는 바람에(심지어 지역이동..) 거주지를 알아보고, 이직회사에서 사용하는 기술을 다시 한번 살펴보는 등 스터디외에도 할게 너무 많아 정신없이 보내고있다. 우선순위를 잘 정해서 2주차를 진행해야할것 같다.정리현재 서포터로도 참여하고 있을 정도로 애정이 정말 많이 가는 스터디다. 이런저런일로 많이 바쁘고 정신이 없지만 조금이라도 함께하는 러너분들에게 도움이 되기 위해 노력해야겠다.

프론트엔드워밍업클럽스터디프론트엔드1기회고발자국

코파

인프런 워밍업 클럽 스터디 1기 FE | 1주차 발자국

목차스터디 참가 계기와 소감 과제 내용앞으로의 방향1. 스터디 참가 계기와 소감 "'HTML, CSS, JS'로 나만의 웹사이트를 만들어 보아요!"라는 유혹적인 문구에 현혹되어 작년 말 웹 개발에 입문하게 되었다. 그리고 작년 크리스마스부터 개강한 모 KDT 국비교육 프론트엔드 부트캠프에 참가하게 되었고, 몇 주 전 수료를 하게 되었다. 4개월이 채 되지 않는 짧은 교육 기간에도 불구하고, 프론트엔드 개발자에게 필수적인 지식은 얼추 쌓을 수 있었다.  하지만 교육과정에서 진행된 두 개의 프로젝트에서 모두 백엔드를 담당하게 되면서 정작 프론트엔드 프로젝트가 하나도 없는 상태로 취업시장에 덩그러니 놓여지게 되었다. 백엔드 프로젝트 리팩토링이나 이력서 작성 등을 고민하며 시간을 낭비하고 있던 와중, 마침 인프런에서 진행하는 스터디를 발견하였다. 이 스터디에 참가하기 위해서는 무려 6만 6천원이라는 비용을 지불해야 했다. 강의 자체보다는 돈을 내고 스터디에 참여한다는 강제성이 큰 자극이 되어 꾸준한 학습을 진행하는 데 도움이 될 것이라고 판단하였다(강의가 수강료에 비해 좋지 않다는 의미가 절대 아니다). 실제로 기한이 주어지는 과제를 통해 평소라면 시작조차 하지 않았을 것들을 구현해보았다. 과제를 수행하면서 문제 정의와 해결 능력을 키울 수 있었다. 또한, 다른 참가자들이 꾸준히 참여하는 모습을 보는 것만으로도 자극을 받는다. 개발초보 취준생인 나부터 회사다니면서 공부하는 경력자분들, 그리고 공부하는 대학생분들까지 모두가 열심히 노력하는 모습을 보며 동기부여에 큰 도움이 되었다. 남은 기간 동안 목표 달성에 힘써야겠다.2. 과제 내용 1) 음식 메뉴https://www.inflearn.com/blogs/7678과제 설명 : 단골 카페인 쉬즈베이글의 메뉴를 만들어보았다. 카테고리별로 나누는 기능(필터링)을 구현하는 것이 핵심이었다.힘들었던 점 : 여태껏 쉬다가 오랜만에 바닐라 자바스크립트를 사용하려고 하니 그 자체가 버거웠다. 특히 자바스크립트로 HTML 요소들을 동적으로 생성하는 부분에서 많은 시간을 소모하였다.소감 : HTML 요소를 동적으로 생성하는 코드를 많이 작성하였다. 이게 효율적인지 의문이 들어서 시간날 때 연구를 해볼 것이다. 2) 가위바위보 게임https://www.inflearn.com/blogs/7768과제 설명 : 가위, 바위, 보 중 하나를 내면 게임이 진행된다. 각 회차별 남은 횟수 및 점수, 마지막 승부 결과와 리셋 버튼까지 구현해보았다.힘들었던 점 : 게임 로직은 단순하여도 여러 요소들을 화면에 렌더링하는 코드를 작성하는 것이 익숙하지 않아서 오래 걸렸다. 단순히 자바스크립트로 가위바위보 게임을 작성하라고 하면 쉬울텐데, 화면에 있는 여러 가지 요소들을 고려해야한다는 점에서 프론트엔드 개발이 쉽지 않다고 느낀다.소감 : Math.random을 활용하여 랜덤 생성하는 것이 어려워서 좀 연습을 해야할 것 같다. 3) 퀴즈(개발 진행중으로 링크 업로드하지 않음 ^^ ><)과제 설명 : 퀴즈 앱은 욕심을 많이 부려서 추가한 기능이 많기 때문에 아직 100% 완성하지는 못하였다. 현재 OX퀴즈에 있었던 큰 이슈를 해결한 상황이다.힘들었던 점 : 과제 중 이것이 가장 어려웠다. 일단 데이터를 불러와서 렌더링해야하는 점부터 어려웠고, 30문제의 데이터 중 10개를 뽑는 로직부터 작성하기 힘들었다. 다음 단계로 넘어갈 때 문제, 정답, 해설을 모두 업데이트 해야하는데, 오류가 매우 많아서 어떤 오류가 있었는지 기억이 잘 나지도 않는다.소감 : 처음부터 로직을 분리하여 추상화된 코드를 작성하고 싶은데 마음 같지 않다. 4) 책 목록https://www.inflearn.com/blogs/7759과제 설명 : 다른 이름의 투두리스트이다. 과제에는 삭제하는 것만 나온 것 같은데, 제대로 된 투두리스트를 만들어본 적이 없어서 수정, 삭제, 추가 기능 (임시저장, CSV 파일 변환, 전체 초기화)도 구현해 보았다.힘들었던 점 : html에서 <table>태그를 처음 다루어보아서 낯설었고, CSV 파일 변환 기능 구현할 때 이슈가 많아서 별도로 정리해두었다.소감 : 일단 CRUD부터 정확하고 빠르게 구현한 후, 추가기능을 고도화하는 방향으로 진행해아 할 것 같다. 리액트도 CRUD부터 빠르고 정확하게 만드는 연습을 해야겠다. 5) 비밀번호 생성https://www.inflearn.com/blogs/7745과제 설명 : 최대 네 가지 조건을 만족하는 비밀번호를 생성하는 어플을 만드는 것이었다. 힘들었던 점 : 기본 아이디어는 네 가지 조건에 해당하는 문자열을 각각 변수에 담아두고, 해당 조건을 체크하면 문자열들을 합친 후 랜덤으로 인덱스를 생성하여 추출하는 방식으로 하였다. 그런데 이러한 경우 체크된 조건에 해당하는 문자가 하나도 없을 수 있기 때문에, 체크된 조건의 문자열은 무조건 무작위로 1개를 뽑은 후에 진행하는 로직으로 변경하였다. 이 부분이 어려웠다.소감 : 배열과 관련된 고차함수를 잘 다루는 것이 중요한 것 같다. 아직 많이 부족해서 꾸준히 공부하려고 한다. 전체 소감변수 선언시 const, let과 scope의 중요성을 명확하게 깨달았다. 이 부분과 관련해서 오류가 많이 났었다. 함수를 작성하더라도 반환값을 리턴하는 것을 간과하거나, 클래스를 생성할 때 constructor가 초기 실행 시점에 작동한다는 점 등 세부사항에 대한 고려가 필요함을 알게 되었다.코드를 작성하면서 로직을 분리하는 것이 얼마나 중요한지를 실감하게 되었다. 코드에 반복되는 패턴이 보인다면 이를 상위 수준으로 추상화하여 정리하는 것이 필요하다. 말은 쉽지만, 내 수준에서는 징그럽게 어렵다.GitHub Finder, 타이핑 테스트, 퀴즈 앱은 화요일까지 완성하려고 한다.3. 앞으로의 방향 1) 수료를 위한 필수적인 목표 바닐라 JS 과제 3개 (완료)리액트 과제 3개발자국 3회 작성 (매주 일요일) : - 발자국에 수업 내용도 정리하기 - 이번주는 수업 내용 정리를 못해서 다음주부터 진행할 예정그룹스터디 2회 참여 및 공부 내용 발표 2) 추가 목표 코드 리팩토링: 기능 개선 및 추가, CSS 적용 (특히 라이브러리 활용), 로직 분리 등을 통한 코드 개선배포 및 정리: 효울적인 코드 고민해보기 

웹 개발워밍업클럽FE발자국1주차

lar

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

1. 1주차 학습한 강의 내용 1일차(1강~5강)1. 새로운 프로젝트 시작하기Spring Initializr 사용하기https://start.spring.io/새로운 프로젝트를 시작할 때 사용하면 된다.의존성 : 프로젝트에서 사용하는 라이브러리와 프레임워크를 의미한다.라이브러리 : 프로그래밍을 개발할 때 미리 만들어져 있는 기능들을 가져와서 사용하는 것을 의미한다.프레임워크 : 프로그래밍을 개발할 때 미리 만들어져 있는 구조에 코드를 가져와서 넣는 것을 의미한다.2. @SpringBootApplication과 서버@SpringBootApplicationpackage com.group.libraryapp; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class LibraryAppApplication { public static void main(String[] args) { SpringApplication.run(LibraryAppApplication.class, args); } }@SpringBootApplication : 어노테이션, 스프링 부트를 자동으로 설정이 가능하다.Class : 메인 메소드로 구성된다.SpringApplication.run(LibraryAppApplication.class, args) : 실제 스프링 부트 애플리케이션을 시작한다는 의미를 가진 코드이다.서버(Server)란?기능을 제공하는 것을 의미한다.어떠한 기능을 제공하는 프로그램을 의미한다.그 프로그램을 실행시키고 있는 컴퓨터를 의미한다.3. 네트워크란 무엇인가?IP : 컴퓨터의 주소를 의미한다.Domain Name(도메인 네임) : 외우기 어려운 IP 대신 외우기 쉬운 이름으로 변환한 것을 의미한다.DNS : IP 244.66.51.9 = 도메인 네임 spring.com, 이러한 체계를 의미한다.port(포트) : 사용하는 프로그램 데이터를 받는다.4. HTTP와 API란 무엇인가?HTTP란?인터넷에서 데이터를 주고 받을 때 하는 표준, 약속을 의미한다.예) 운송장 표준 - 요청하는 행위, 받는 사람, 항목(자원), 세부조건행위와 자원은 운송장을 보내기 전에 약속을 해야 한다.HTTP 요청GET 요청GET /portion?color=red&count=2 Host: spring.com:3000 //의미 - 내놓아라 파란집 둘째, 포션 빨간색 2개 GET(HTTP Method) : 요청을 받는 컴퓨터에게 요청하는 행위(데이터 요청)/portion(Path) : 받을 항목(자원)? : 구분기호color=red : 자원의 세부조건(Query)& : 구분기호count=2 : 자원의 세부조건(Query)Host: spring.com:3000 : HTTP 요청을 받는 컴퓨터와 프로그램 정보POST 요청POST/oak/leather Host: spring.com:3000 오크가죽정보 //창고에 넣어라 빨간집, 오크가죽 POST : 요청을 받는 컴퓨터에게 요청하는 행위(데이터 저장)/oak/leather : HTTP 요청을 받는 컴퓨터에게 원하는 자원Host: spring.com:3000 : 어떤 컴퓨터에 어떤 데이터를 받을지 정보오크가죽정보 : 실제 저장할 오크 가죽 정보(데이터, Body)데이터를 보내는 2가지의 방법쿼리 : GET에서 사용한다.바디 : POST에서 사용한다.다양한 HTTP MethodGET : 데이터 요청, 쿼리 사용POST : 데이터 저장, 바디 사용PUT : 데이터 수정, 바디 사용DELETE : 데이터 삭제, 쿼리 사용API(Application Programming Inteface)란?정해진 약속을 통해 특정 기능을 수행하는 것을 의미한다.HTTP 응답정보 처리해서 응답 보내기(200 OK) -> 200 OK : 정보가 잘 저장되었다는 의미이다.저장이라는 기능을 수행한다.💡 요청에 대한 응답을 제공한 컴퓨터는 서버(Server)를 의미한다.💡요청한 컴퓨터는 클라이언트(Client)를 의미한다.상대 코드응답에 들어가는 숫자를 의미하며, 매우 다양하다.어떠한 상태인지 알려주는 코드이다.상대 코드의 종류200 OK : 요청이 성공했다는 의미이다.300 Moved Permanently : 다른 곳으로 옮겨가라는 의미이다.404 NotFound : 요청한 내용을 찾을 수 없다라는 의미이다.500 Internal Server Error : 내부에 문제가 발생했다는 의미이다.5. GET API 개발하고 테스트 하기덧셈 APIHTTP Method -> GETHTTP Path -> /add쿼리(key와 value) -> int number1 / int number2API의 반환 결과 -> 숫자 - 두 숫자의 덧셈 결과GET API 개발 데이터의 흐름Postman에서 ?number1=100&number2=200(쿼리) 스프링 부트에서 보내면 값을 보고 객체로 만들어둔 Calculator, Add, Request에다가 값을 넣는다.그 객체를 Controller, API의 진입지점에 보내준다.정보를 전달하는 역할의 객체 : DTO(Data Transfer Object)Postman에서 send를 하면, HTTP 요청 -> 스프링 부트 서버로 도착 -> API 진입지점을 거쳐 /Add 통과 후 DTO request 함수를 실행한다.return -> 응답 생성 -> Postman에게 전달 -> Postman에서 결과를 보여준다. 2일차(6강~9강)1. POST API 개발 및 테스트하기POST API에서 데이터 받을 경우HTTP Body 사용한다.JSON으로 데이터를 받는다.사용되는 문법 -> JSONJSON이란?객체 표기법을 의미하며, 무언가를 표현하기 위한 형식이다.JSON 문법{}중괄호를 사용한다.{}중괄호 안에 "key":value로 표기한다.속성은 ,로 구분한다.추가적으로 다른 JSON 문법을 작성할 수 있다.{ "name":"김철수", "age":50 } 곱셈 APIHTTP Method -> POSTHTTP Path -> /multiplyHTTP Body(JSON) -> {"number1":숫자,"number2":숫자}API의 반환 결과 -> 숫자(곱셈 결과)POST API 개발 데이터 흐름데이터를 전달해주는 객체인 DTO 생성POST API 개발Body 사용 시 @RequestBody 어노테이션을 사용한다.JSON을 CalculatorMultiplyRequest 객체로 전환해준다. 2. 유저 생성 API 개발 및 테스트하기도서 관리 애플리케이션의 요구사항사용자도서관 사용자 등록(이름 필수, 나이 선택)도서관 사용자 목록 확인도서관 사용자 이름 수정도서관 사용자 삭제책도서관 책 등록 및 삭제도서관 사용자 책 대여다른 사람이 대여 시 대여 불가능도서관 사용자 책 반납도서관 사용자 등록하기유저 생성 API 조건HTTP Method : POSTHTTP Path : /userHTTP Body (JSON)JSON { "name": String (null 불가능), "age":Integer } 결과 반환 X(HTTP 상태 200 OK이면 충분하다.)유저 생성 API 개발 데이터 흐름Body를 객체로 표현할 DTO 생성유저를 저장하기 위한 객체 생성유저 생성 API 개발POST user 호출 -> 함수 실행 -> Body에 이름과 나이가 들어오면 객체로 매핑한다.새로운 유저를 만들 때 사용되는 Requset -> 생성된 유저 객체가 List에 저장 -> 함수가 예외없이 완료되면 응답코드 200 OK로 반환한다.3. 유저 조회 API 개발 및 테스트하기유저 조회 API 조건HTTP Method : GETHTTP Path : /user쿼리 : 없음결과 반환 (JSON)JSON { "name": String (null 불가능), "age":Integer } 유저 조회 API 개발 데이터 흐름데이터를 담아 줄 DTO 생성유저 조회 API 개발User List 생성 -> List에 들어있는 유저들이 1개씩 돌면서 UserResponse 형태로 반환한다.결과 리스트에 추가 -> responses로 반환한다. 3일차(10강~13강)1. 컴퓨터의 핵심 부품 이해하기서버 유저 정보가 왜 남아있지 않는지 이해하기 위해 알아보자.CPU : 연산RAM : 임시 기억장치DISK : 장기 기억장치2. Database와 MYSQLDatabase란?데이터를 구조화시켜 저장하는 것을 의미한다.RDB (Relational Database) - MySQL : 데이터를 표처럼 구조회 시켜 저장하는 것을 의미한다.SQL(Structured Query Language) : 표처럼 구조화된 데이터를 조회하는 언어이다.3. MySQL에서 Table 생성하기데이터베이스 생성create database [데이터베이스 이름]; 데이터베이스 목록 확인show databases; 데이터베이스 삭제drop database [데이터베이스 이름]; 데이터베이스 안으로 들어가기use [데이터베이스 이름]; 테이블 목록 확인show tables; 테이블 생성create table [테이블 이름] ( [필드1 이름] [타입] [부가조건], [필드1 이름] [타입] [부가조건], ... primary key([필드 이름]) ); 테이블 생성 예시create table fruit ( id bigint auto_increment, name varchar(20), price int, stocked_date date, primary key (id) ); auto_increment : 데이터가 없어도 1부터 1개씩 증가하며, 자동 기록된다.primary key : 유일한 키를 의미하며, id라는 필드를 지정한다.테이블 삭제drop table [테이블 이름]; => 해당 SQL를 DDL(Data Definition Language)이라고 정의한다.MYSQL 타입의 종류정수 타입tinyint : 1바이트int : 4바이트bigint : 8바이트실수 타입double : 8바이트 정수decimal(A, B) : 소수점을 B개 갖고 있는 전체 A자릿수 실수문자열 타입char() : ()글자가 들어갈 수 있는 문자열varchar() : 최대 ()글자가 들어갈 수 있는 문자열날짜, 시간 타입date : 날짜, yyyy-MM-ddtime : 시간, HH:mm:ssdatetime : 날짜와 시간을 합친 타입, yyyy-MM-dd HH:mm:ss4. Table 데이터 조작하기생성, 조회, 수정, 삭제 방법(CRUD)생성 : Create읽기 : Read수정 : Update삭제 : Delete데이터 삽입insert into [테이블 이름](필드1 이름, 필드2 이름) values (값1, 값2, ...) 데이터 삽입 예시insert into fruit (name, price, stocked_date) values ('사과', 1000, '2023-01-01'); ()안의 필드와 값의 순서가 중요하기 때문에 순서대로 작성해야 한다.id는 지정하지 않아도 auto_increment가 자동으로 생성해준다.데이터 조회select * from [테이블 이름]; select * from [테이블 이름] where [조건]; 데이터 조회 예시select name, price from fruit; select * from fruit where name = '사과' and price <= 2000; 테이블명 대신 필드 이름도 가능하며, 여러개를 넣을 수도 있다.조건을 넣어서 조회도 가능하다. (and, or, =,>=,<=, !=, between, in, not 등이 있다.)데이터 수정update [테이블 이름] set 필드1이름=값, 필드2이름=값, ... where [조건]; 데이터 수정 예시update fruit set price = 1500 where name = '사과'; 조건을 붙이지 않으면 모든 데이터가 업데이트 되기 때문에 주의 해야 한다.데이터 삭제delete from [테이블 이름] where [조건]; 데이터 삭제 예시delete from fruit where name = '사과'; 조건을 붙이지 않으면 모든 데이터가 삭제 되기 때문에 주의 해야 한다.=> 해당 SQL를 DML(Data Manipulation Language)이라고 정의한다.5. Spring에서 Database 사용하기Database 설정 파일 추가application.yml 파일 생성spring: datasource: url: "jdbc:mysql://localhost/library" username: "root" password: "" driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://Host/접근DB명username: MySQL에 접근하기 위한 계정명password: MySQL에 접근하기 위한 비밀번호driver-class-name: DB에 접근 시 사용할 프로그램 4일차(14강~16강)1. 사용자 수정 API, 삭제 API 개발 및 테스트하기사용자 수정 API 조건HTTP Method : PUTHTTP Path : /userHTTP Body (JSON){ "id": Long, "name": String } 결과 반환 X (HTTP 상태 200 OK이면 충분하다.)사용자 삭제 API 조건HTTP Method : DELETEHTTP Path : /userQuery 사용문자열 name (삭제되어야 하는 사용자 이름)결과 반환 X (HTTP 상태 200 OK이면 충분하다.)사용자 수정 및 삭제 API 개발사용자를 수정하기 위한 DTO 추가사용자 수정/삭제 API 개발사용자 수정 API, 삭제 API 예외처리 하기존재하지 않는 사용자를 수정하거나 삭제하려고 해도 응답코드 200 OK가 나온다.> 예외처리해야 한다.데이터 존재 여부를 확인하고 예외처리 하기사용자 수정/삭제 API 예외처리 개발id를 기준으로 사용자가 존재하는지 확인하기 위해 SELECT 쿼리 작성한다.SQL을 DB에 전송해서 데이터가 있는지 확인한다.SELECT SQL의 결과가 있으면 0 반환, ? 값 id를 삽입한다.0은 최종적으로 List로 반환한다. 사용자가 존재하지 않을 경우 예외처리한다.사용자가 존재하지 않을 경우예외처리한 결과, 내부에 문제가 있다는 오류가 발생했다. 5일차(17강~18강)1. 좋은 코드(Clean Code)가 왜 중요한가?코드는 요구사항을 표현하는 언어이다. 좋은 코드는 코드만 보고도 의미를 파악을 할 수 있다.안 좋은 코드가 쌓이면, 시간이 지날수록 생산성이 낮아진다.Clean Code함수는 최대한 작게 만들고, 1가지 일만 수행하는 것이 좋다.클래스는 작아야 하며, 하나의 책임만을 가져야 한다.2. Controller, Service, Repository 분리하기Controller의 함수 1개가 하고 있던 역할API의 진입 지점으로서 HTTP Body를 객체로 변환한다 -> Controller현재 유저가 있는지 없는지 등을 확인하고 예외처리를 해준다. -> ServiceSQL을 사용해 실제 DB와의 통신을 담당한다. -> Repository역할 분리 구조Controller : API와 HTTP 관련 역할 담당Service : 분기 처리 및 로직 담당Repository : DB와의 접근을 담당각 역할을 분리하여 수행하는 계층으로 이루어진 구조를 계층화 아키텍쳐(Layered Architecture)라고 한다.JdbcTemplate 변수를 선언하고 생성자를 통해 UserRepository를 인스턴스화하는 시점에 JdbcTemplate을 넣어주도록 변경한다.2. 미션첫 번째 과제(1일차)나만의 어노테이션을 만들 수 있었다는 걸 알게 되었고, 어떠한 상황에서 정해진 어노테이션만 사용했었는데, 만드는 방법을 알게 되어 좋았다.두 번째 과제(2일차)3번째 문제에서 배열로 처리했는데, 배열보다는 리스트로 하는 게 더 좋다는 피드백 댓글을 보고 리스트로 적용해서 구현해보고, 다른 방법들도 알게 되어 좋았다.세 번째 과제(3일차)람다식에 대해 깊게 공부한 적은 없었는데, 이번 과제를 하면서 람다식에 대한 도입 배경, 방법 등을 알게 되어 좋았다.3. 회고일주일이 정말 금방 지나간 느낌이다. 과제를 하면서 스스로 고민해보고 구현하면서 성장할 수 있는 시간을 갖게 되어 좋았다. 다른 일도 겹치면서 아직 4,5일차 과제를 완료하지 못했지만 기간 안에 제출하도록 노력해야겠다. 헷갈리는 부분들을 복습하면서 다음주엔 더 분발해야겠다!

백엔드발자국회고워밍업1주차

장종원

[인프런 워밍업 클럽 1기/BE] 첫번째 발자국

인프런 워밍업 클럽 1기 첫번째 발자국을 찍으며..이 스터디를 시작하게 된 동기는 직접 찾은 것은 아니고 친구의 추천으로 시작하게 되었다.공부를 해보지 않았던 분야는 아니지만, 성격상 동기부여 될 것이 있으면 좀 더 공부를 열심히 할 수 있을 것 같다는 생각이 들었던 것 같다. 그리고 마침 오래되어 까먹기도 많이 한 것 같아 다시 한 번 상기하면 좋을 것 같았다.이번 스터디로 공부에 대한 마음을 다시 한 번 잡고, 계획적으로 공부하는 습관을 기르기에 좋을 것 같다는 생각에 열심히 해보려 첫번째 발자국을 남긴다.섹션 1. 생애 최초 API 만들기어노테이션마법같은 일(설정 등)을 자동으로 해줌@SpringBootApplication : 스프링을 실행시킬 때 필요한 다양한 설정들을 자동으로 해줌서버컴퓨터가 특정 기능을 수행해준다.그래서 컴퓨터 자체를 서버라고도 함서버에 요청을 할 땐 인터넷(네트워크)으로 하게 됨네트워크현실세계에는 컴퓨터별 고유 주소(ip)가 존재인터넷을 통해 데이터를 주고 받음port : 특정 port를 사용하는 프로그램은 반드시 하나Domain name : ip주소는 외우기 어려우니 ‘이름’을 쓰는것 ⇒ 이러한 체계를 DNS(Domain Name System)이라함HTTP(TyperText Transfer Protocol)데이터를 주고 받는 표준. protocol : 표준/약속 GET : HTTP Method, 요청을 받는 컴퓨터에게 데이터를 달라고하는 것POST : 요청을 받는 컴퓨터에게 요청하는 행위, 원하는 자원을 적어줌. 실제 저장할 정보가 포함됨, 행위와 자원은 HTTP 요청 전 약속되어있어야함HTTP 요청을 받는 컴퓨터와 프로그램 정보 Http Method + path + Query(GET) OR Body(POST)PUT : 데이터를 수정하라(Body) / DELETE : 데이터 삭제하라(Query)HTTP 요청 문법(첫줄) 메소드 패스 쿼리 + HTTP 버전(다음 줄) 헤더(여러줄 가능)(한줄띄고 그다음 줄) 바디(여러줄 가능)HTTP 응답정상 처리되었다면 200 OK(상태코드)를 보낸쪽에 보냄응답에는 추가 정보(바디)를 담을 수도 있음300 - 다른 곳으로 옮겨라, 404 - 요청한 것찾을 수 없다, 500 - 내부에 문제가 생겼음요청에 대한 응답을 제공(serve)한 컴퓨터가 바로 서버요청을 한 컴퓨터가 Client(고객)문법(첫줄) 상태코드(여러줄) - 헤더한 줄 띄기(여러 줄) - 바디API(Application Programming Interface : 규칙)정해진 약속을 하여 특정 기능을 수행하는 것클라이언트와 서버가 HTTP를 주고 받으며 기능을 동작하는데 정해진 규칙을 API라 함URL(Uniform Resource Locator)주소창@RestController이 클래스를 API의 진입지점으로 만들어 줌그 안에 메소드를 만들어서 그걸 API가 사용하게끔 만들어줄 수 있음@GetMappingapi의 GET@RequestParam쿼리라고 명시주어지는 쿼리를 함수 파라미터에 넣음@RequestParam을 제거하고 클래스로 대체할 수 있음DTO(Data Transfer Object) 역할을 한 것임 POST API => HTTP Body를 이용한다. 이 때 사용되는 문법이 "JSON"JSON(JavaScriptBojectNotation) : 객체 표기법{ “name” : “최태현”, “age” : 99, “dogs” : [”코코”, “초코”], “house” : { ”address” : “서울” } } 한 Controller Class에 여러 API 추가 가능@PostMapping 어노테이션 사용@RequestBody : HTTP Body로 들어오는 JSON을 객체로 변경해준다.이 때 객체의 필드이름과 JSON key의 이름이 같아야 함 Controller에서 getter가 있는 객체를 반환하면 JSON이 된다.public class Fruit { public String getName() { return name; } public long getPrice() { return price; } }이게 가능한 이유는 @RestController가 붙어있기에 가능하다. 섹션2. 생애 최초 Database 조작하기테이블 만들기crate table [테이블 이름] ( [필드1 이름] [타입] [부가조건], [필드2 이름] [타입] [부가조건], ... primary key([필드이름]) );MySQL 타입 - 정수타입tinyint : 1바이트 정수int : 4바이트 정수bigint : 8바이트 정수실수타입double : 8바이트 정수decimal(A, B) : 소수점을 B개 가지고 있는 전체 A자리수 실수Decimal(4,2) = 12.23문자열 타입char(A) : A글자가 들어갈 수 있는 문자열varchar(A) : 최대 A 글자가 들어갈 수 있는 문자열날짜/시간 타입date : 날짜, yyyy-MM-ddtime : 시간, HH:mm:ssdatetime : 날짜와 시간을 합친 타입, yyyy-MM-dd HH:mm:ss위의 SQL이 DDL(Data Definition Language)아래는 DML(Data Manipulation Language)조회 Queryselect * from fruit where id = 1 and price <= 2000;select * from fruit where id = 1 or price <= 2000;select * from fruit where price BETWEEN 1000 AND 2000;select * from fruit where name IN ('사과', '수박');select * from fruit where name NOT IN ('사과', '수박');업데이트 Queryupdate fruit set price = 1500 where name = '사과';where 조건을 주지 않고 실행할 수 있으나, 모든 경우를 모두 변경할 수 있으니 조심해야 함삭제 Querydelete from fruit where name = '사과';where 조건을 붙이지 않으면 모든 데이터가 삭제됨. 주의필요우리의 스프링 서버가 MySQL DB에 접근하게 하자application.yml 파일에 DB 관련 설정을 적어서 가능private final JdbcTemplate jdbcTemplate; public UserController(JdbcTemplate jdbcTemplate) { this.jdbcTemplate = jdbcTemplate; }jdbcTemplate을 이용해 SQL을 쓸 수 있다. 생성자를 만들어 jdbcTemplate을 파라미터로 넣으면, 자동으로 들어간다.jdbcTemplate.update() 는 INSERT/UPDATE/DELETE 쿼리에 모두 사용할 수 있다.첫 파라미터로 sql을 받고, ?를 대신할 값을 차례로 넣으면 된다.return jdbcTemplate.query(sql, new RowMapper<UserResponse>() { @Override public UserResponse mapRow(ResultSet rs, int rowNum) throws SQLException { long id = rs.getLong("id"); String name = rs.getString("name"); int age = rs.getInt("age"); return new UserResponse(id, name, age); } });RowMapper : SQL 쿼리결과를 <>안에 있는 타입으로 변경시켜주는 역할하는 함수람다를 사용해서 간단하게 변경할 수 있었음HTTP Method PUT/DELETEjdbcTemplate.update() 는 sql의 업데이트가 아니라 데이터에 변화가 있는걸 말함없는 유저를 업데이트/삭제하려 해도 200 OK가 나오는 게 문제!자바에선 Exception/Throw로 예외를 던져 처리할 수 있었음.스프링에서 매핑된 함수가 throw 에러를 하면 200 OK 대신 500 Internal Server Error가 나온다.⇒ 데이터 존재 여뷰를 확인하고 예외를 던지면 되겠다jdbcTemplate.query() 를 사용하면 반환값이 List로 감싸진다. 섹션3. 역할의 분리와 스프링 컨테이너Clean Code는 왜 중요한가?현업에서 코드를 읽고 조금 변경하거나 조금의 새 기능을 추가하는 일이 더 많다.그렇기에 코드를 읽는 것을 피할 수 없다. 잘 읽을 수 있도록 하는 능력을 키우는 것도 중요하다.내가 다른 사람의 코드를 읽듯이 다른 사람도 내 코드를 읽게되므로 나 또한 가독성이 좋은 코드를 작성하는 것도 중요하다.왜 한 Controller에서 모든 기능을 구현하면 안될까?<Clean Code>함수는 최대한 작게 만들고 한 가지 일만 수행하는 것이 좋다.클래스는 작아야하며 하나의 책임만을 가져야 한다.반대된 다면 문제가 될 이유함수를 동시에 여러 명이 수정할 수 없다그 함수를 읽고 이해하는 것이 어렵다함수의 일부분을 수정하더라도 함수 전체에 영향을 미칠 수 있어, 수정을 함부로 하기 어렵다.너무 큰 기능이므로 테스트하기 어렵다.종합적인 유지보수성이 매우 떨어진다.Controller의 함수 1개가 하고 있던 역할API 진입지점현재 유저가 있는지 확인 후 예외처리SQL을 사용하여 DB와 통신Controller - API, HTTP 관련 역할 담당, Service를 사용Service - 분기 처리, 로직 담당, Repository를 사용Repository - DB와의 접근 담당이러한 것을 Layered Architecture라고 한다. ※ 트러블슈팅강의내용은 spring boot 2.x버전과 java 11사용으로 시작되었으나, 강의 시작 기준 spring initializr 기준 spring boot 3.x 버전과 java 17 버전을 사용할 수 밖에 없었음spring boot 3.2.5 + java 17 버전 연동(?) 문제(기존 java-home이 java 11이었고, spring boot 3.x 버전부턴 java 17을 꼭 사용해야 해서 발생한 오류)intellij 프로젝트 구조에서 java version을 변경하고, 시스템 환경변수에서 java home을 17버전으로 변경하여 해결mysql workbench safe mode 해제참고 : https://blog.naver.com/kkson50/221251167091@RequestBody에 매핑되는 DTO는 왜 빈 생성자가 필요한가참고 : https://innu3368.tistory.com/181 과제1. 어노테이션을 사용하는 이유(효과)는 무엇일까?https://slime-feels-660.notion.site/acb06609050b490a8d1de99c6395e8dd?pvs=4지금까지 어노테이션들을 너무 자연스럽게 쓰다보니 왜 쓰는지나 개념적인 부분을 생각하지 않고 너무 편해서 기능만 알고 쓴 것 같았는데 조사하다보니 단순 어노테이션이라는 키워드 하나에도 공부할 수 있는 부분이 상당히 많고 깊었다.앞으로도 단순히 사용법만 알고 기능을 사용하기보다 왜? 라는 것에 초점을 맞춰 공부해야 한다는 생각이 들었다.과제2. API 만들기https://slime-feels-660.notion.site/14c2b5efc21c46cd9ec46124704b31ea?pvs=42-1, 2-2 과제를 할 때는 생각보다 금방 했는데, 2-3번 과제를 할 때 다 만들어놓고 해결되지 않는 것을 이해를 못했다.구글링을 통해 결국 이유를 찾았지만 쉽다 생각한 것에도 또 무언가 있었다. 얕게 공부하기보다 깊게 공부할 수 있도록 해야겠다는 마음을 다시 먹게 만들어 준 과제같았다. 과제3. 자바의 람다식은 왜 등장했을까? 람다식과 익명 클래스는 어떤 관계가 있을까? 람다식의 문법은 어떻게 될까?https://slime-feels-660.notion.site/742bd212b32e4830b6fea014dc70c443?pvs=4람다식을 사실 많이 쓰진 않은 것 같다. 익숙한 걸 쓴다 해야할까 쓰면 코드량이 줄고 여러 기능들이 추가되지만 자연스럽게 코딩을 하다보면 편한 방법을 쓰게 된다. 추가적으로 스터디를 해서 내 익숙한 코딩방법을 좋은 방법으로 교정하는 작업이 필요하겠다는 생각을 하게했다.마무리1주를 돌아보며 생각보다 시간이 부족하다. 발자국을 위해, 복습을 위해 내용을 노션에 적으면서 수강을 하니 이 정도 시간이 들겠지 하고 계획한 것보다 시간을 더 쓰게 됐었다. 시간이 있는 날은 여유가 있네 생각하고 바로 쉬는 것보다 조금 더 예습을 해두는 게 공부하기로 계획한 것을 밀리지 않고 계속 해낼 수 있을 것 같아 그렇게 해보려 한다.아, 그리고 실수로 5/3 특강을 날짜를 착각해서 시간을 놓치게 되었는데 너무 아쉽고 17일날에는 꼭 알람을 해놓고 들어야겠다.공부를 하며, 강의를 들으며 더 깊게 공부하고 싶은 것들이 종종 보였는데, 그런 것들도 키워드를 모아 시간을 내 공부하여 궁금한 것들을 채워나가야겠다. 

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

정진서

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

첫 걸음인프런 워밍업 클럽 1기 BE에서 자바와 스프링 부트로 생애 최초 서버 만들기, 누구나 쉽게 개발부터 배포까지! [서버 개발 올인원 패키지] 강의를 수강하며 작성하였다.강의 학습HTTP와 APIHTTP란 다른 컴퓨터로 데이터를 보내기 위한 데이터 표준이다.API는 정해진 약속에 따라 특정 기능을 수행하는 코드이다. HTTP Method 중 하나인 GET은 쿼리를 통해 정보를 보낸 후 데이터를 요청한다.→ /add?number1=10&number2=20 Controller는 API가 실행되는 입구와 같다.@GetMapping("/add") public int addTwoNumbers(@RequestParam int number1, @RequestParam int number2) { return number1 + number2; } GET 방식에서 매개변수를 각각 하나씩 입력받을 때에는 @RequestParam을 사용하여 변수에 쿼리의 값을 넣는다.만약 매개변수가 많을 경우, DTO 객체로 만들어 관리한다.@Getter public class CalculatorAddRequest { private final int number1; private final int number2; public CalculatorAddRequest(int number1, int number2) { this.number1 = number1; this.number2 = number2; } }GET 방식의 DTO 객체는 반드시 생성자를 포함해야 한다. DTO 객체를 이용한 Controller는 다음과 같다.@GetMapping("/add") public int addTwoNumbers(CalculatorAddRequest request) { return request.getNumber1() + request.getNumber2(); }DTO를 이용할 때는 @RequestParam을 제거해야 하며 변수를 객체로 감싸 불러오기 때문에 getter를 사용해야 한다. 마찬가지로 HTTP Method인 POST는 Body를 통해 정보를 보낸다.객체 표기법인 JSON을 사용하고, List를 사용하거나 JSON 안에 JSON을 사용하는 것도 가능하다.POST 방식에서 DTO를 이용한 Controller는 다음과 같다.@PostMapping("/multiply") public int multiplyTwoNumbers(@RequestBody CalculatorMultiplyRequest request) { return request.getNumber1() * request.getNumber2(); } @RequestBody를 사용하여 HTTP Body로 들어오는 JSON을 DTO 객체 형태로 변환한다.GET 요청의 @RequestParam과는 달리 DTO 객체를 사용해도 Annotaion을 생략할 수 없다. GET 요청과 달리 POST 요청은 생성자가 필요하지 않다. Domain(Entity)와 DTO의 차이DTO는 계층 간 데이터 교환을 위해 사용된다. 반면, Domain은 DB 테이블과 매핑되어 데이터를 저장하거나 관리하는 실제 비즈니스 도메인을 표현한다. DTO는 단순히 어떤 데이터로 통신할 것인지 정의하고, 상세한 정의는 Domain에서 한다. Spring에서 Database 사용하기private final JdbcTemplate jdbcTemplate; public UserController(JdbcTemplate jdbcTemplate) { this.jdbcTemplate = jdbcTemplate; } @PostMapping("/user") public void saveUser(@RequestBody UserCreateRequest request) { String sql = "insert into user (name, age) values (?, ?)"; jdbcTemplate.update(sql, request.getName(), request.getAge()); }JdbcTemplate를 이용하면 Spring에 SQL을 전달할 수 있다.SQL문을 작성하여 문자열 변수로 저장하는데, 값이 들어갈 부분에 ?를 사용하면 데이터를 넣을 수 있다.  과제 수행1일차 과제 : JAVA Annotation어노테이션은 실제 데이터가 아닌 메타데이터로써 클래스와 메서드에 추가하여 다양한 기능을 부여한다.반복적인 코드 작성을 줄일 수 있어 코드량이 감소하고, 코드의 역할을 명확히 지정할 수 있어 유지보수가 용이하다는 장점이 있다.사용자가 원하는 기능을 수행하는 커스텀 어노테이션을 만드는 방법도 알아보았다. 2일차 과제 : GET & POST API날짜를 받는 API를 구현할 때, String으로 날짜를 받은 후 LocalDate로 변환하는 과정을 거쳤다.그러나, 스프링 부트 2버전에서는 @DateTimeFormat을 사용해 LocalDate를 바로 받을 수 있다고 한다.3버전대에서는 어노테이션 없이 LocalDate를 바로 받을 수 있다. 3일차 과제 : 람다식과 익명 클래스익명 클래스란 이미 정의되어 있는 부모 클래스의 자원을 일회성으로 재정의한 클래스이며, 자식 클래스를 정의할 필요 없이 객체화가 가능하다.람다란 자바의 인터페이스를 익명 클래스로 구현한 익명 구현 객체를 짧게 표현한 것이다. 메서드 타입과 이름, 매개변수 타입, 중괄호, return문을 생략하고 화살표 기호를 넣는다. 4일차 과제 : 과일 가게 API 만들기enum 타입을 통해 과일의 판매 여부를 ‘SOLD’, ‘NOT_SOLD’의 두 가지 상태로 구분하였다.COALESCE()를 사용하여 팔린 과일 혹은 팔리지 않은 과일이 존재하지 않을 경우, NULL이 아닌 0으로 표시되도록 하였다.쿼리의 결과가 하나이므로 queryForObject를 이용해 SQL의 결과를 직접 long 타입의 객체로 매핑하였다.  느낀 점Spring의 기초를 복습하면서 ‘이건 왜 이렇게 쓰일까’와 같은 질문을 던지며 천천히 학습할 수 있었다.특히, 인프런의 각 강의에 있는 질문 게시판을 활용하였다.강의를 들으며 의문이 든 부분들은 이미 선배 러너들이 질문을 했고, 강의자 분께서 자세하게 답변을 달아 주셨기 때문에 편하게 학습할 수 있었다. 매일 과제가 있지만 하루에 학습할 양을 안내해 주는 진도표가 존재하고, 하루치 공부 양이 그렇게 많지 않았기 때문에 오히려 꾸준하게 하루 공부를 진행할 수 있어 좋았다.일주일을 돌아보며 작성하는 발자국은 그동안 배운 내용을 정리하고, 느낀 점을 회고할 수 있어 좋은 방법인 것 같다.강의를 들으며 개조식으로 정리한 내용을 글로 풀어 쓰니 내가 얼만큼 이해하고 있는지 확인할 수도 있다.

백엔드워밍업클럽BE발자국1주차

강예인

[인프런 워밍업 클럽_1기 BE] 첫번째 발자국

강의 수강일주일 동안 학습했던 내용IntelliJ설치, Spring 환경 설정, MySQL 설치개발을 위한 기본적인 환경설정을 해주었다. 네트워크 기초API 통신을 위한 기본적인 API동작 방식에 대해 학습했다.데이터베이스데이터베이스 세팅과 CRUD에 대해 학습했다.API 제작GET,POST,PUT,DELETE를 써서 사용자를 등록하고 수정하고 삭제하고 조회했다.사용자 정보가 없을 경우에 대해 예외처리를 했다.클린 코드클린코드가 왜 중요한지에 대해 학습했다.Controller가 하나의 기능만 하도록 분리하는 작업을 했다.미션1. 어노테이션어노테이션이 무엇인지와 장단점에 대해 알아보고 나만의 어노테이션을 만들어보았다.미션2. API 실습두 수의 합,차,곱을 반환하는 API제작LocalDate를 활용하여 날짜 요청시 요일 반환 API제작배열 안의 숫자를 모두 더하는 API 제작미션3. 람다식자바에서 람다식이 왜 생겨났는지와 람다식 문법을 알아보았다.회고참가 계기내가 주로 쓰고 있는 언어는 javascript와 C#인데 한국에서는 java를 주로 써서 블로그를 봐도 java에 대한 내용이 더 많이 보이기도 했고 코딩 테스트에서나 개발자를 채용할 때도 대부분 java가 기준이였다. 그래서 나도 한 번 java를 사용해서 서버를 만들어볼까 생각했었고 마침 인프런 워밍업 클럽 1기 스터디 모집 글을 보게 됐다. java를 안해본지 좀 오래됐는데 마지막으로 java를 쓴지는 한...3년 전이였나 기억도 나질 않는다. 그래도 기초부터 시작하니 커리큘럼대로 따라가기만 하면 문제 없을 것 같아서 신청하게 되었다.새로운 것IntelliJ  내가 처음에 java를 배웠을 때 Eclipse를 썼는데 이젠 IntelliJ를 쓰나보다. 솔직히 java는 내 관심 영역은 아니었기에 현재 쓰고 있는 컴퓨터엔 전혀 세팅이 되어 있지도 않았다. 내가 친숙한건 vscode,Visual Studio 이것 뿐.. Spring boot도 처음 써본다... mysql?  mysql은 써보긴했는데 이것도 대학생 때 썼으니 오래 전이긴하다. Oracle이랑 비슷해서 문제는 없긴한데 bigint 타입이 있어서 이건 뭐지? 했는데 ORACLE에서 NUMBER(19)를 표현한 거였다. 조금씩 다른데 적다가 헷갈리긴한다. 강의에서 IntelliJ에 DB연결해서 사용하는 부분이 있었는데 유료라서 굳이 돈을 써야할까 싶어서 CLI 창으로 했다. 미션미션 1. 어노테이션어노테이션이 무엇인지와 나만의 어노테이션을 만들어보는 미션이 있었는데 어노테이션이라는 개념을 처음 들어봤다. 그래서 찾아봤는데 C#에서 어튜리뷰트와 좀 비슷한 것 같았다. 이번에 flutter도 시작했는데 dart에도 어노테이션을 지원한다는 것을 알게됐다. java에서 annotation을 만들어보면서 다른 언어에는 어떻게 작동하는지 비교하면서 해결했다.미션2. API 실습  문제2번에서 C#에서 DateTime이 java에서 localDate를 쓰는 것을 확인했다. 작동 방식은 비슷한 것 같았다. 처음에 요청 받을 때 LocalDate로 받는 방식을 썼었다. 에러가 나서 설마 LocalDate로 받아와서 그런가 싶어서 String으로 바꿨었는데 결국 오타 때문에 안된 거였었다. 근데 바꾸기 귀찮아서 다시 안바꿨다. 과제가 끝나고 멘토님이 스프링 부트 2.x.x 버전대에서는 @DateTimeFormat 이라는 어노테이션을 사용해서 LocalDate 를 바로 받을 수 있고 3.x.x 버전 대에서는 어노테이션 추가 없이 LocalDate를 받을 수 있다고 답변 달아주셔서 String으로 받은게 조금 찔려서 다시 수정했다.미션3. 람다식자바에서 람다식에 대한 과제를 내주셨을 때 C#과 javascript,dart에서 쓰고있어서 반가웠다. C# LINQ와 비슷한 스트림(Stream) API도 알게 되었다. 각 언어별로 람다식으로 예제를 만들어서 연습해봤다.느낀점이번주 동안 강의를 듣고 미션을 수행하면서 각 언어 별로 어떻게 다른지 비교하는 방식을 통해 오히려 java에 대한 내용 뿐만 아니라 다른 언어를 이해하는데 도움이 된 것 같다. 언어 별로 독특한 기능과 차이점을 보면서 언어마다 어떤 문제를 해결하기위해 도입했는지 스토리를 찾아보는게 재밌었다.이번주 동안 진도표와 과제제출을 성실하게 수행한 나한테 칭찬한다..ㅎㅎ진도표와 과제제출을 통해 나태한 나에게 압박감을 주는 스터디가 나한테 맞는 것 같다.다음주에도 열심히 따라가야지!!

백엔드발자국인프런워밍업클럽1기BE

김 동현

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

1주차 후기우선 일주일 간 자바스크립트 강의를 모두 보면서 5개의 과제를 클리어한 나와 다른분들에게 박수를보낸다 (짝짝짝) 백엔드 0기를 참여했을때보다 더 힘들고 정신이없었던것같다. 기존에 백엔드를 공부하고있었고 자바스크립트는 이미 기억저편으로 잊혀진지 오래여서 기억을 더듬어가고 오랜만에 검색엔진을 돌리면서 코딩을 하니 시간이 더 오래걸렸다. 이번 프론트엔드 워밍업클럽을 끝내고 백엔드 프론트 모두 내손으로만든 포트폴리오를 뽑아내겠다는 일념으로 참여했는데자바스크립트가 예상보다 어려워서 놀랐다. (왜 오타가 컴파일 에러가 안나지? 같은거?) 자바와 인텔리제이 단축키에 익숙해진 나는 vscode에서 쓸수있는단축키가 ctrl + c , ctrl + v , ctrl + ` 정도 밖에없었다.  지금은 더 찾아보면서 써서 조금 더 편하게 쓰고는 있지만 아직 익숙해질려면 갈길이 먼듯하다.그래도 수정하면서 바로바로 화면에 반영되는건 조금 감동적이었다. 수정해도 터미널에 코드덩어리 밖에 나오지않는 백엔드와는 확실히 다른 재미인것같다.  1주차는 코드를 잘짜는것보다는 돌아가는 바퀴를 만드는데에 집중했던 시간이었다.어떤 코드가 잘 작성한 코드인지 주로 어떻게 작성하는지에 대한 케이스가 없으니 함수를 나열해놓게되고 각 요소들을 그냥 변수로 잡아서 무분별하게 사용했다. 이런점에서는 코드리뷰를 좀 받고 다른분들 코드를 많이 읽으면서 케이스를 쌓아가야할것같다.  앞으로2주차는 리액트를 들어가면서 또 새로운 방법에 적응하는 시간이 필요할것같다. 그리고 스터디도 시작할듯 한데 서로 코드리뷰를 하면서 다른분들 코드에서 좋은점을 보고 배워서 적용해서 점점 나은 결과물이 만들어지는 과정을 즐겨보려한다.   

프론트엔드워밍업클럽FE1기발자국

이슬

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

The four Fs 🍀FACTS (사실, 객관)이번 일주일 동안 있었던 일, 내가 한 일인프런 워밍업 클럽 스터디 1기를 시작했다. 1:1 멘토링권과 인프콘 초대권이 너무나 탐났기에 우수러너를 목표로 JavaScript 강의를 시작했다.스터디를 시작하면서 Firebase, React.js로 채팅앱 만들기 강의도 듣고 있다. 강의의 기술스택을 활용하여 React Query와 함께 해결할 새로운 개인 프로젝트를 생각해두고 있다.스터디 과제로 음식메뉴, 가위바위보, 퀴즈 앱을 바닐라JS로 만들었다.프론트엔드 개발직무로 이력서를 꾸준히 지원하고 있는데, 아직 연락은 없었다..😢FEELINGS (느낌, 주관)나의 감정적인 반응, 느낌, 어려움생각했던 것보다 스터디에 쏟는 시간이 길어서 2주차 때에는 시간관리에 더 신경써야겠다고 느꼈다.올해 초에 모던자바스크립트 딥다이브 책으로 한번 1독을 해서 그런지 호이스팅, 스코프, 프로토타입 섹션은 힘들지않게 이해했다. (과거의 나.. 칭찬해..)과제로 DOM 메서드 사용하며 작업했는데 거의 1년만에 사용하다보니 낯설었고, 기능을 만들 때 배열이나 객체의 어떤 프로토타입 메서드를 사용하는게 나을지 고민하는 것이 재밌었다. + 코딩테스트 문제를 푸는 기분도 들었다. FINDINGS (배운 것)그 상황으로부터 내가 배운 것, 얻은 것, 가장 인상 깊었던 배움기억에 남는 배움으로는 this와 this를 바인딩하는 bind, call, apply 메서드, 이벤트 위임이었다. 특히 이벤트 위임은 과제를 하면서 바로 바로 적용시켰기 때문에 앞으로도 잘 활용할 것 같다.복습하고 싶은 배움으로는 Design Pattern, Intersection observer, 커링이었다. 특히 Intersection observer로 lazy loading을 구현할 수 있다는 걸 이번에 알았기 때문에 더 기억에 남을 것 같다. 커링은 문법으로는 봤었지만 어떤 용어로 부르는지 몰라서 궁금증이 해소되었다.과제를 통해 DOM Node를 직접 변수로 설정하고 값을 바꾸고 업데이트하는 작업을 했는데, 첫 설계의 중요성을 알게되었다. 마음이 급해 기능을 위한 코드를 작성하다가 요소의 위치를 다시 설정해야하는 순간이 있었다. 기능도 중요하지만, 기능을 적용시킬 요소들을 어떻게 배치하고 설계해야할지 처음부터 잘 고민을 해야겠다고 느꼈다.FUTURE (미래)현재 나의 상태와 배운 것을 미래에는 어떻게 적용할 지이번 주가 끝나면 다시 React.js를 공부하게 될텐데, 이번 기회에 모국어(JavaScript)를 복습하게 되어서 좋았다. 이번 주에 배웠던 부분을 곰곰이 복기해보면서 아직 끝내지 못한 나머지 과제들에 녹여보도록 하겠다.2주차에는 강의를 빠르게 보고, 과제를 하며 고민하는 데에 시간을 더 써보는 연습을 해야겠다. 화이팅!

웹 개발인프런워밍업클럽FE1기발자국