블로그
전체 4#태그
- 인프런워밍업클럽스터디
2024. 10. 03.
0
[인프런 워밍업 클럽 스터디] Day 4 미션
1. 아래 코드와 설명을 보고, [섹션 3. 논리, 사고의 흐름]에서 이야기하는 내용을 중심으로 읽기 좋은 코드로 리팩토링해 봅시다.public boolean validateOrder(Order order) { if (order.checkItemsNone()) { log.info("주문 항목이 없습니다."); return false; } if (order.getTotalPrice() 2. SOLID에 대하여 자기만의 언어로 정리해 봅시다.SOLID객체지향 설계를 더 이해하기 쉽고 유연한 형태로, 유지 보수를 쉽게 만드는 데 도움을 주는 원칙 SRP: Single Responsibility Principal 단일 책임 원칙하나의 클래스가 하나의 책임만 갖도록 설계함객체의 공개 메서드, 필드, 상수 등이 객체의 단일 책임에 의해서만 변경되어야 한다. 상수: 동일한 퍼블릭 상수를 두 가지 객체가 사용하는 경우를 고려한다.관심사의 분리와도 일맥상통높은 응집도(클래스, 모듈 등이 긴밀하게 연결되어 있는 정도), 낮은 결합도(협력 관계의 2개 이상 객체에서, 한 객체가 변경될 때 다른 객체가 영향 받는 정도/ 의존성의 최소화라고도 함)*책임의 경계선을 인식하기 위해서는 계속해서 질문을 던져 보는 경험적 측면이 필요. OCP: Open-Closed Principle 개방-폐쇄 원칙새로운 요구 사항의 추가와 같은 변동 사항 발생 시, 기존 코드 변경 없이 시스템의 기능을 확장한다.추상화와 다형성을 통해 지킬 수 있다.*난이도 조절 기능이 추가된다면> (1) 숫자 변경되었을 때 그 숫자에 맞춰 게임이 정상 진행되도록 한다 (2) 난이도 조절에 대한 요구사항에 대응한다의 순서로 기능을 추가해 나간다. LSP: Liskov Substitution Principle 리스코프 치환 원칙상속 구조에서 부모 클래스 인스턴스를 자식 클래스 인스턴스로 치환할 수 있어야 한다.일반적으로부모 클래스보다 자식 클래스의 기능이 더 많다. 이때 부모 클래스가 기능하는 것을 자식 클래스가 해도 동일하게 동작해야 한다는 것.LSP를 위반하면 상속 클래스 사용 시 예외가 발생하거나, 불필요한 타입 체크가 필요할 수 있다. ISP: Interface Segregation Principle 인터페이스 분리 원칙클라이언트는 자신이 사용하지 않는 인터페이스에 의존해서는 안된다. 즉 인터페이스를 기능 단위로 잘게 나눠야 한다.ISP를 위반하면 결합도가 높아지고, 특정 기능 변경이 여러 클래스에 영향을 미칠 수 있다. DIP: Dependency Inversion Principle 의존성 역전 원칙상위 수준 모듈이 하위 수준 모듈에 직접 의존하지 않고, 두 개 모두 추상화에 의존해야 한다.상위 수준 모듈: 추상화 레벨이 높은 쪽하위 수준 모듈: 구체에 가까운 쪽 *의존성: 하나의 모듈이 다른 하나 모듈을 알고 있거나 직접 사용하는 모든 것들의존성의 순방향: 고수준 모듈이 저수준 모듈을 참조하는 것의존성의 역방향: (고수준 모듈) 기능을 추상화해서 추상화된 스펙을 만족하는 것을 이용해 요구사항을 해결할 수 있는 것. (저수준 모듈) 인터페이스를 구현한 구현체가 얼마든지 바뀔 수 있는 것.즉, 추상화를 가운데 두고 의존하도록 하는 것 *DIP, DI, IoCDIP(Dependency Inversion Principle): 고수준 모듈과 저수준 모듈은 직접적 의존이 아닌 추상화에 의존해야 한다.DI(Dependency Injection): 필요한 의존성을 외부에서 주입받는다. "3"의 숫자를 기억할 것. 객체 a, b가 있는 상황에서 a가 b를 필요로 할 때 직접 생성자를 통해 주입 받는 것이 아닌, 제3자가 둘 사이의 의존성을 맺어준다. 스프링에서는 스프링 컨텍스트(**IoC 컨테이너)가 한다.IoC(Inversion of Control): 프로그램 제어 주도권이 개발자가 아닌 프레임워크에 있다.**IoC 컨테이너: 객체의 생명주기를 관리하고 의존성 주입까지 해 준다.
인프런워밍업클럽스터디
2024. 05. 19.
0
[인프런 워밍업 클럽 스터디] BE 1기 세번째 발자국
섹션 5. 책 요구사항 구현하기 JPA에서 연관관계를 매핑하기연관관계의 주인: "상대 테이블을 가리키는 테이블"Table을 바라보았을 때 누가 관계의 주도권을 가지고 있는지를 의미함.JPA에도 알려주는 법: 연관관계의 주인이 아닌 객체에서 @OneToMany와 같은 연관관계 어노테이션에 mappedBy 옵션을 표시한다.연관관계 주인의 효과: 연관관계의 주인을 기준으로 테이블이 연결된다.예를 들어, 연관관계의 주인인 person 은 setAddress 를 통해 테이블을 이어주지만, address로 같은 메서드를 실행하면 제대로 테이블이 연결되지 않는다.person.setAddress(address); // 정상 실행 address.setPerson(person);연관관계의 주인을 통해 객체를 이어줘도, 반대쪽도 이어지는 것은 아니므로 하나의 setter 안에서 객체끼리 완전히 연결시켜 주어야 한다.연관관계의 주인인 person 은 setAddress 내에서 address의 Person도 설정해 준다.public void setAddress(Address address) { this.address = address; this.address.setPerson(this); }연관관계를 나타내는 어노테이션 @OneToMany와 @ManyToOne: N : 1 관계@OneToOne: 1 : 1 관계@ManyToMany: N : M 관계 (*N : M 연관관계의 구조는 복잡하고, 테이블도 직관적으로 매핑되지 않기 때문에 사용하지 않는 게 좋음) JPA 연관관계의 다양한 옵션 mappedBy 옵션: 연관관계의 주인이 아닌 객체가 주인에게 매여 있음을 표시한다.@JoinColumn: 연관관계의 주인에게 활용할 수 있는 어노테이션. 연관관계의 주인이 가지고 있는 다른 테이블을 가리키는 필드의 이름이나 null 여부, 유일성 여부, 업데이트 가능 여부 등을 정해줄 수 있다.cascade 옵션: 한 객체가 저장되거나 삭제될 때, 연결되어 있는 객체도 함께 저장되거나 삭제되는 기능orphanRemoval 옵션: 연관관계가 끊어진 데이터를 자동으로 제거해 준다.fetch 옵션: 객체의지연 로딩 여부를 설정한다. 꼭 필요할 때 가져오는 LAZY 옵션(기본 옵션), 처음 데이터를 로딩할 때 바로 가져오는 EAGER 옵션이 있다.지연 로딩: 영속성 컨텍스트의 특징. 필요한 순간에 연결되어 있는 객체를 가져온다.연관관계를 사용하면?도메인 계층에서 두 클래스가 직접 협업할 수 있으므로 Service 계층의 코드가 더 간결해진다.하지만 연관관계를 너무 지나치게 사용하면 시스템 파악이 어려워지고 한 곳에서의 수정이 다른 곳까지 영향을 줄 수도 있으므로 여러 부분을 고민하며 연관관계 사용 여부를 결정해야 한다.섹션 6. 생애 최초 배포 준비하기 배포란 무엇인가 필요한 프로그램(ex. spring, MySql..)이 설치된 전용 컴퓨터에 코드를 옮기고 실행하는 과정profile 설정하기똑같은 서버 코드를 local profile에서는 H2 DB로, dev profile에서는 MySQL DB를 사용하도록 설정할 수 있다.application.yml의 설정 값도 함께 변경해 주어야 함. git과 githubgit: 코드를 쉽게 관리할 수 있도록 해주는 버전 관리 프로그램github: git으로 관리되는 프로젝트의 코드가 저장되는 저장소코드를 github 저장소에 저장하는 과정git add .: 모든 코드를 담는다.git commit -m "메시지": 파일들을 포장하고 적고 싶은 문장을 적는다.git push: 송장을 붙인 파일들을 github에 보낸다.EC2: Elastic Compute Cloud의 약자로, 탄력적으로 원격 컴퓨터를 사용할 수 있다는 의미.섹션 7. 생애 최초 배포하기리눅스 명령어mikdir 폴더이름 - 폴더를 만든다ls - 현재 위치에서 폴더나 파일을 확인한다ls -l - 폴더의 자세한 정보를 확인한다 접근권한 명령어가 표시된다. 이를 변경하는 명령어가 chmod다. cd 폴더이름 - 폴더 안에 들어간다.cd .. - 현재 위치에서 상위 폴더로 간다.pwd - 현재 위치를 확인한다. rmdir 폴더이름 - 비어있는 디렉토리를 제거할 수 있도록 한다.foreground와 backgroundforeground: 우리가 보고 있는 프로그램background: 우리가 보고 있지 않은데 실행 중인 프로그램EC2 접속을 종료해도 서버가 동작하게 하려면 서버가 background에서 동작할 수 있도록 명령어를 추가해야 한다.jar 파일 실행 명령어의 앞뒤로 nohup [명령어] &를 붙여준다.ex. nohup java -jar build/libs/library-app-0.0.1-SNAPSHOT.jar --spring.profiles.active=dev &rm nohup.out - nohup.out 파일을 제거한다.nohup.out 파일을 확인하는 방법리눅스 편집기 vim을 활용: vi nohup.out 입력접속한 터미널에서 확인: cat nohup.out or tail -f nohup.outbackground로 동작하고 있는 서버 종료하기리눅스 작업 관리자 명령어 ps aux를 사용해 실행 프로그램 목록을 볼 수 있다. (*java 관련 프로그램만 보고 싶다면 ps aux | grep java)프로그램마다 고유한 번호가 있으므로 kill -9 번호를 입력하면 서버 프로그램이 종료된다.섹션 8. Spring Boot의 이모저모 Spring Boot와 관련된 여러가지 사항build.gradle:gradle을 이용해 프로젝트를 빌드하고 의존성을 관리하기 위해 작성한 파일Spring과 Spring Boot Spring Boot는 Spring에서 제공하는 편리한 기능을 사용하기 위해 필요한 설정을 Java의 어노테이션 기반으로 가능하게 하고, 기본적인 설정들은 모두 자동으로 해준다.Spring Boot의 starter로 의존성 관리가 쉬워졌고, starter 추가만으로 원하는 기술을 쉽게 도입할 수 있게 되었다. application.yml과 application.properties:스프링 설정에 활용된다. YAML 방법이 가독성이 좋다.lombok : getter나 생성자, setter, equals, toString 등을 자동으로 만들어준다.어노테이션 @Getter, @NoArgsConstructor, @RequiredArgsConstructor(final이 붙어 있어 필수로 값 필요한 변수들을 가지고 있는 생성자를 만든다), @Setter, @EqualsAndHashCode, @ToString 등이 있다.완성한 서버를 배포해 보는 경험을 처음 경험해 본 일주일이었다. 강의에서 제공된 완벽한 코드를 실행시킨 것 뿐이지만, 지금까지 했던 코드의 터미널 실행과는 또 다른 느낌의 기쁨을 느낄 수 있었다. 깃 허브는 사실 아직도 잘 모르겠지만, 첫 시작을 했으니 다음에 사용할 때는 조금 더 여유롭게 사용할 수 있을 것 같다.연관관계를 따져보는 내용은 디비 수업에서 배웠던 것의 연장선에 있는 듯한데, 비슷하면서도 개념이 완전히 같지는 않아서 실제로 개발할 때 공부가 더 필요할 부분인 듯하다.본 강의 내용은 이번 주로 드디어 끝이 났다. 아직 남은 부분이 있긴 하지만, 이렇게 빠듯한 일정으로 강의를 다 수강해 본 게 얼마 만 인지 모르겠다. 남은 내용들도 잘 챙겨가서 다음 단계 공부에 잘 활용해 보고 싶다. 과제6API 3단 분리, Repository 분리하기API 분리는 전에 해두었던 것을 좀 더 간결하고 가독성 좋게 만드는 방식으로 만들었다.Repository의 경우 원래 만든 Repository의 볼륨이 상대적으로 컸기에 잘못 분리하면 에러 파티가 날 수도 있겠다 싶어서 살짝 부담스러웠지만 그래도 잘 분리할 수 있었다.과제7기존 코드를 JPA를 통해 동작하도록 변경하기왠지 모르겠지만 굉장히 오래 걸렸다. 첫 몇 시간은 아예 과제 주제를 착각해서 허송세월을 보내기도 했다. 차근차근 강의록을 다시 읽어보면서 내용을 과제에 적용하려고 했다.기존 코드는 쿼리를 직접 작성해야 했기에 문자열 " "안에 작성한다는 것이 매우 부담스러웠는데, 훨씬 간편해졌다. 다른 분들의 코드도 몇 개 참고했는데 JPA에서 제공하는 기능으로 쿼리를 원하는 대로 작성해서 데이터를 뽑아낸 경우도 있었다. 그런데 복잡해 보이기도 하고 나는 강의에서 다뤄진 내용으로 해보고 싶어서 기본 메소드를 사용했다. 나중에 필요할 때가 분명 있을 테니 그 때 활용해 보기로 했다.앞선 과제를 계속해서 더 간결하고 편리하게 사용할 수 있는 쪽으로 개선하는 과제가 많았는데, 이 과제들이 아니었다면 난 곧장 JPA만을 이용해 코드를 짰을 것이다.(아마도) 차근차근 순서대로 같은 기능을 하는 다른 코드들을 시간을 들여 작성해 보는 시간들이 매우 소중하다는 걸 느낀 과제였다.
인프런워밍업클럽스터디
2024. 05. 12.
0
[인프런 워밍업 클럽 스터디] BE 1기 두번째 발자국
섹션 3. 역할의 분리와 스프링 컨테이너스프링 컨테이너와 스프링 빈@RestController: 강의 중에 다루는 Controller 클래스를 스프링 빈으로 등록한다.스프링 빈: 스프링 컨테이너 안에 들어간 것. 클래스에 대한 다양한 정보 저장&인스턴스화가 이루어진다. 이때 필요한 의존성이 자동으로 설정되어 JdbcTemplate를 통한 인스턴스화가 가능하다.@Repository: Repository를 스프링 빈으로 등록한다.@Service: Service를 스프링 빈으로 등록한다.스프링 컨테이너의 필요성만약 필요에 따라 Repository를 구분하여 작성하게 된다면, Service 계층에서 어떤 Repository를 사용할지 수정해 주어야 한다. 큰 규모의 프로젝트에서는 담당하는 Repository를 변경하는 것이 번거롭기 때문에 Service 계층의 코드를 바꾸지 않고 Repository만을 변경할 방법을 고안해야 한다.이것에 대한 해결책이 스프링 컨테이너인 것.제어의 역전(IoC, Inversion of Control): 컨테이너가 필요한 Repository를 선택하고, Service를 만들어 주는 것.의존성 주입(Dependency Injection): Service를 만들 때 Repository 중 하나를 선택해 넣어주는 과정@Primary: 우선권 제어. 이 어노테이션이 붙은 Repository를 선택한다.스프링 빈을 다루는 법 스프링 빈으로 등록하기 @Service와 @Repository: 개발자가 만든 클래스를 스프링 빈으로 등록할 때 사용@Configuration와 @Bean: 외부 라이브러리나 프레임워크에 만들어져 있는 클래스를 스프링 빈으로 등록할 때 사용@Configuration : 클래스에 붙이는 어노테이션. @Bean을 사용할 때 함께 사용. @Bean : 메소드에 붙이는 어노테이션. 메소드에서 반환되는 객체를 스프링 빈에 등록.@Component: 컨트롤러, 서비스, 리포지토리 외의 추가적인 클래스를 스프링 빈으로 등록할 때 사용. 주어진 클래스를 컴포넌트로 간주한다. 스프링 서버가 사용될 때 자동으로 감지된다. 스프링 빈을 주입받기 생성자 이용하기: 가장 간단, 권장되는 방법ex. JdbcTemplate jdbcTemplatepublic UserRepository(JdbcTemplate jdbcTemplate) { this.jdbcTemplate = jdbcTemplate; }setter 사용하기: final 키워드 제거, 메소드에 @Autowired 붙이기필드에 직접 주입: 필드 위에 바로 @Autowired를 적어준다. @Qualifier: @Primary 어노테이션이 없는 상황에서 주입받는 쪽이 특정 스프링 빈을 선택함.@Primary와 @Qualifier를 모두 사용하고 있다면?: @Qualifier를 사용한다.섹션 4. 생애 최초 JPA 사용하기1-1. 문자열 SQL을 직접 사용하는 것의 한계문자열 작성에 실수가 있을 수 있고, 이를 인지하는 시점이 느림: 런타임 오류로 이어질 수 있다.특정 DB에 종속적: 특정 DB를 사용하다가 다른 종류의 DB로 바꿔야한다면 번거롭다.많은 반복작업: 많은 수의 쿼리를 작성해야 하고, SELECT 쿼리 시에는 필드 매핑이 번거롭다.DB의 테이블과 객체의 패러다임이 다르다: DB는 절차지향적이라면, 객체는 객체지향적. 1-2. 문자열 SQL의 한계에 대한 해결책JPA(Java Persistence API): 객체와 관계형 데이터베이스의 테이블을 짝지어 데이터를 영구적으로 저장할 수 있도록 정해진 Java 진영의 규칙(interface)Hibernate: JPA는 API이기 때문에 규칙일 뿐이고, 이 규칙대로 코드를 작성한 가장 유명한 프레임워크Spring Data JPA: SQL을 작성하지 않아도 쿼리가 나갈 수 있도록 자동으로 처리해준다. Spring Data JPA를 이용해 데이터를 생성, 조회, 수정, 삭제하기스프링은 JpaRepository를 구현 받는 Repository에 대해 자동으로 SimpleJpaRepository 기능을 사용할 수 있게 해 준다. SimpleJpaRepository에 있는 대표적인 메소드는 다음과 같다.save: 주어지는 객체를 저장하거나 업데이트findAll: 주어진 객체가 매핑된 테이블의 모든 데이터 가져오기findById: id를 기준으로 특정한 1개의 데이터 가져오기복잡한 JPA 코드를 직접 사용하는 게 아니라, 추상화된 기능으로써 사용하게 된다.Spring Data JPA를 이용해 다양한 쿼리 작성하기ex. 유저 삭제하기: 이름을 이용해 유저 존재 여부 확인/ 유저가 존재한다면 delete 쿼리 날리기UserRepository 인터페이스User: 이름을 기준으로 유저 데이터 조회해서 유저 객체 반환. 유저가 없다면 null이 반환됨findByName: 함수 이름만 작성하면, 알아서 select * from user where name = ?라는 SQL이 조립된다.find: 1개의 데이터를 가져옴.By: 뒤에 붙는 필드 이름으로 SELECT 쿼리의 WHERE 문이 작성된다 public interface UserRepository extends JpaRepository { User findByName(String name); }UserRepository: 기본적으로 들어있는 delete 메소드를 사용한다.public void deleteUser(String name) { User user = userRepository.findByName(name); if (user == null) { throw new IllegalArgumentException(); } userRepository.delete(user); }Spring Data JPA의 추가적인 쿼리 작성법By 앞find: 반환 타입은 객체 or OptionalfindAll: 쿼리의 결과물이 N개인 경우 사용 반환 타입은 List 이다.exists: 쿼리 결과가 존재하는지를 확인. 반환 타입은 boolean이다.count: SQL의 결과 개수를 센다. 반환 타입은 long이다.By 뒤: 필드 이름이 들어가고, 이들을 And나 Or로 조합할 수 있다. 동등 조건(=) 외 다양한 조건 활용도 가능함.findAllByNameOrAge 라 작성하게 되면, select * from user name = ? or age = ? 라는 쿼리가 나간다. 트랜잭션의 필요성과 스프링에서 트랜잭션을 제어하는 방법트랜잭션: 여러 SQL을 사용해야 할 때 한 번에 성공시키거나, 하나라도 실패하면 모두 실패시키는 기능commit: 트랜잭션 시작 후 사용된 SQL을 성공적으로 반영한다rollback: 트랜잭션 시작 후 사용된 SQL을 모두 취소한다.스프링에서 트랜잭션을 제어하는 방법: 대상 메소드에 @Transactional 어노테이션을 붙여준다.@Transactional 어노테이션데이터의 변경이 없고, 조회 기능만 있을 때는 @Transactional(readOnly = true)로 설정할 수도 있다.Unchecked Exception에 대해서만 롤백이 일어난다. (IOException과 같은 Checked Exception에 대해서는 일어나지 않음)영속성 컨텍스트: 테이블과 매핑된 Entity 객체를 관리/보관하는 역할 수행 스프링의 경우, 트랜잭션을 사용하면 영속성 컨텍스트가 생겨 나고, 트랜잭션이 종료되면 영속성 컨텍스트가 종료된다.영속성 컨텍스트의 특징변경 감지 (Dirty Check): Entity는 명시적으로 save 해주지 않아도 알아서 변경을 감지하여 저장한다.쓰기 지연: 트랜잭션이 commit 되는 시점에 SQL을 모아서 한 번만 날린다.1차 캐싱: ID를 기준으로 Entity를 기억한다. 섹션 5. 책 요구사항 구현하기책 생성, 대출, 반납 API를 온전히 개발하며 지금까지 다루었던 모든 개념을 실습해 본 다.책 생성, 대출, 반납 API 개발하기 책 생성 API:book 테이블을 설계하고, Book 객체를 만들고, Repository, Service, Controller, DTO를 만든다. book 테이블 명세:create table book( id bigint auto_increment, name varchar(255), primary key (id) );책 대출 API: 어떤 유저가 어떤 책을 반납했는지 확인할 수 있는 user_loan_history 테이블을 추가로 만들고, 책이 대출되었는지 확인 후 대출되지 않아야 대출할 수 있다.user_loan_history 테이블 명세:create table user_loan_history ( id bigint auto_increment, user_id bigint, book_name varchar(255), /* 유저가 빌린 책을 대출 중인지, 반납 완료했는지 확인하는 필드이다. 이 필드에 0이 들어가 있으면 대출 중인 것이고, 1이 들어가 있으면 반납한 것이다. tinyint는 객체와 매핑되면 true인 경우 1이, false인 경우 0이 저장된다. */ is_return tinyint(1), primary key (id) )user_loan_history 테이블에 대응되는 객체 loanhistory 패키지와 Repository를 만든다.서비스 계층: 책 이름으로 책을 가져오고, 책이 없는 경우를 확인해 예외처리한다. Book 객체를 확인하고 대출 기록을 확인한다. 대출되지 않았다면 유저 객체를 가져오고 대출 기록을 저장한다.책 반납 API대출 기능에서 사용하는 HTTP Body 스펙이 유저 이름과 책 이름으로 동일하지만, 새로운 DTO를 만들어 주는 것이 추후 side-effect의 발생을 줄인다.유저 이름을 찾아 유저 객체를 받아오고 유저 아이디와 책 이름으로 대출 기록 객체를 받는다. 해당 책의 is_return 값을 '1'로 바꾼다.API 개발을 계속해서 더 객체지향적으로 발전시키는 법을 배웠다. 아직도 SQL을 API에 가져와 쓰는 것이 익숙하지는 않지만, 이번주 학습 내용에 포함된 JPA를 자유롭게 다룰 수 있으면 훨씬 보기 쉽고 안정적인 서버를 개발할 수 있을 것 같다.특히 책 대출 기능 개발 부분이 어려웠는데, exists 메소드를 작성할 때 exist로 오타를 내서 스프링 서버가 동작하지 않아 아찔했던 기억이 있다. 여러 개의 클래스, 패키지 등을 다루는 만큼 더 꼼꼼하게 작성하도록 주의를 기울여야겠다.이번주는 평일에 이런저런 일들이 많아 제시간에 강의를 듣지 못한 날이 있다. 다음주가 마지막인만큼 하루에 들어야 할 강의는 꼭 그 날 해결할 수 있도록 할 것이다. 과제4과일 가게 운영을 표현하는 API를 만들기3단 분리를 해서 만들었는데, 알고 보니 과제6이 관련된 과제였어서 조금 후회되었다. 그래도 작성한 코드가 완벽하게 분리된 것은 아니어서 다음 과제에서는 더 다듬어진 코드로 만들 예정이다.SQL문을 통해서는 테이블에서 필요한 값을 뽑아내는 것이지, SQL문을 통해 모든 것이 더 계산된 결과를 받는 것이 아니다. 필요한 계산은 Repository 내에서 일어나도록 코드를 작성한다. 과제5클린 코드로 리팩토링하기강의 중에서도 클린 코드에 대한 언급이 간략하게 있지만, 직접 구글링으로 클린 코드에 대해 자세히 정리한 글을 읽으며 다시 한번 복습했다. 주어진 과제 코드를 리팩토링하면서 앞서 읽은 클린 코드에 대한 지식을 적용해 보려고 노력했다. (변수명, 클래스명, 함수명 짓기와 함수의 역할 분리)과제 코드는 간단한 동작을 하는 것이기 때문에 클래스와 메인 함수를 같은 java 파일 내에서 작성했지만, 규모가 더 큰 프로젝트라면 지금 하고 있는 도서 관리 어플리케이션처럼 많은 패키지와 클래스로 나눠서 작성하여 더 클린하게 코드를 작성하도록 유의해야 한다.클린 코드에 대한 것은 어렴풋이 알고 있었던 것이지만 의식해서 '클린 코드로 만들어보자'고 작성한 것은 이번 과제가 처음이라 새로운 경험이었다. 앞으로도 클린 코드 작성 원칙을 잊지 않도록 유념해야겠다. 프로그래밍 과제가 많아 힘들었지만, 해결했을 때의 뿌듯함은 역시 프로그래밍 과제만 한 것이 없는 것 같다. 남은 과제들도 모두 잘 수행해 보겠다.
인프런워밍업클럽스터디
2024. 05. 05.
0
[인프런 워밍업 클럽 스터디] BE 1기 첫번째 발자국
Section 1. 생애 최초 API 만들기1. 스프링 부트 프로젝트를 설정해 시작하고 실행하는 방법만들어져 있는 스프링 프로젝트를 다운로드 받아 IntelliJ를 통해 여는 경우: INntelliJ 실행 후 첫 화면의 Open을 통해 가져온다.메인 함수가 있는 클래스 옆에 있는 초록색 세모 버튼을 눌러 Run을 선택하면 실행된다.아무것도 없는 상태에서 새로 스프링 프로젝트를 시작하는 경우: https://start.spring.io/에 접속해 요소를 각각 설정해 준다.Project: 빌드 툴을 고르는 항목Language: 서버 개발 시 사용할 언어Spring Boot: 스프링 부트의 버전. 옆에 알파벳이 붙지 않은, 가장 최신 버전을 고르는 게 좋다.Project Metadata: 프로젝트에 사용될 다양한 이름을 설정하는 항목. Java의 버전은 11이 가장 많이 사용된다.Dependencies: 프로젝트에서 사용하는 라이브러리(*미리 만들어져 있는 기능을 가져다 사용하는 것)이나 프레임워크(*프로그래밍 개발 시 미리 만들어져 있는 구조에 코드를 가져다 끼워 넣는 것)위와 같은 설정을 마치고 Generate를 통해 프로젝트를 만들면 압축 파일이 다운로드 되고, 그 후의 과정은 '만들어져 있는 스프링 프로젝트를 다운로드 받아 IntelliJ를 통해 여는 경우'와 동일하다. 2. 서버 개발에 필요한 다양한 개념서버: 어떠한 기능을 제공하는 프로그램. 그 프로그램을 실행시키고 있는 컴퓨터네트워크: 인터넷을 통해 데이터를 주고 받게 하는 시스템HTTP: 웹을 통한 컴퓨터 간의 통신에 대한 표준화된 방식HTTP 요청: HTTP Method(GET, POST)와 Path(/portion)이 핵심이며, 요청에서 데이터를 전달하기 위한 방법은 쿼리와 바디이다. HTTP 응답: 상태 코드 API: 클라이언트와 서버는 HTTP를 주고 받으며 기능을 동작하고, 이때 정해진 규칙이다. 3. 스프링 부트를 이용한 GET API / POST API 개발API Specification(명세): API를 개발하기 전에 API의 HTTP method, HTTP path, 쿼리와 바디, API의 반환 결과를 결정해야 한다.GET API쿼리로 들어오는 값의 개수가 많아진다면? 즉 @RequestParam이 많아지면 코드가 길어지고, 실수의 여지가 생기므로 쿼리를 받는 Class를 만든다. DTO(Data Transfer Object) 객체: '쿼리'를 서버 안 Controller로 전달하는 역할POST API쿼리를 사용하지 않고 바디(Body)를 사용한다.Json: 바디에 데이터를 담아주는 방식 { "name": "최태현", "age": 99 }POST API의 경우에는 Body를 통해 데이터가 들어오기 때문에 @ReqestBody를 꼭 사용해주어야 한다.HTPP Body는 @ReqestBody를 통해 DTO에 매핑되고, Controller로 들어가 최종 결과를 반환한다.유저 생성 & 조회 API 개발유저 조회 시 필요한 JSON으로 결과 반환하기: Controller에서 그냥 객체를 반환하면, JSON으로 응답이 간다. 이때 객체에는 getter가 있어야 한다. Section 2. 생애 Database 조작하기디스크와 메모리의 차이, Database의 필요성 컴퓨터의 핵심 부품 세 가지: CPY는 연산담당, DISK는 장기기록, RAM(메모리)는 단기기억의 역할을 수행한다.서버를 실행시켜 API를 동작시키는 과정스프링부트 서버가 DISK에 파일로 존재한다. 서버를 실행하면 DISK의 코드 정보가 RAM으로 복사된다. API가 실행되면 연산이 수행되고, CPU와 RAM을 왔다 갔다 한다. (ex) POST API를 통해 생긴 유저 정보는 RAM에 쓰여 있다. 서버가 종료되면 RAM에 있는 모든 정보가 사라지므로 재시작하면 유저 정보가 없는 것. DISK에 정보를 저장하기 위해 DB를 사용하는 것. MySQL Database를 SQL과 함께 조작하기DDL(Data Definition Language): 데이터를 정의하는 SQLDBcreate database [데이터베이스 이름]; show databases; drop database [데이터베이스 이름]; use [데이터베이스 이름];테이블create table [테이블 이름] ( [필드1 이름] [타입] [부가조건], [필드 2 이름] [타입] [부가조건], ... primary key ([필드 이름]) ); show tables; drop table [테이블 이름];DML(Data Manipulation Language): 데이터를 조작하기 위한 SQLCRUDCreate(생성): 데이터를 넣는다Retrieve or Read(읽기): 조회한다Update(업데이트): 수정한다Delete(제거): 삭제한다# 데이터 넣기 INSERT INTO [테이블 이름] (필드1이름, 필드2이름, ...) VALUES (값1, 값2, ...) # 데이터 조회하기 SELECT * FROM [테이블 이름] WHERE [조건]; # 데이터 업데이트하기 UPDATE [테이블 이름] SET 필드1이름=값1, 필드2이름=값2, ... WHERE [조건]; # 데이터 삭제하기 DELETE FROM [테이블 이름] WHERE [조건]; 스프링 서버를 이용해 Database에 접근하고 데이터를 저장, 조회, 업데이트, 삭제하기스프링 서버와 DB 연결: application.yml 파일을 생성해 스프링 서버와 연결할 DB 정보를 설정한다.DB를 통해 데이터를 저장, 조회하기POST API를 DB를 이용하게 변경하기 (Controller)jdbcTemplate을 이용해 MySQL에 SQL을 보낸다."INSERT INTO user(name, age) VALUES(?, ?)"; 와 같이 값이 들어가야 하는 부분에는 ?를 사용한다.jdbcTemplate.update(sql, request.getName(), request.getAge());jdbcTemplate.update는 INSERT, UPDATE, DELETE 쿼리에 사용 가능첫 파라미터는 sql, ?를 대신할 값을 차례로 넣어줌GET API를 DB를 이용하게 변경하기 (Controller)jdbcTemplate.query(sql, RowMapper 구현 익명클래스): query를 사용하면 SELECT 쿼리를 날릴 수 있다.@Override 함수: ResultSet 객체에는 결과가 담겨있고, 이 객체에 getType("필드이름") 을 사용해서 실제 값을 가져온다.람다를 이용해 더 간결한 코드 작성도 가능하다.UPDATE API, DELETE API 예외 처리: 대상이 없으면 IllegalArgumentException과 같은 표준 예외를 throw해야한다.기본적인 코드 구조: 유저(대상)이 있는지 id나 name을 통해 확인한다→ 유저 데이터가 있다면 비어 있지 않은 리스트를 받아오고, 유저 데이터가 없다면 비어있는 리스트가 반환된다.→ if문으로 검사해서 유저가 존재하지 않으면 IllegalArgumentException을 던진다. Section 3. 역할의 분리와 스프링 컨테이너좋은 코드(Clean Code)의 중요성코드를 작성하는 시간보다 읽는 시간이 훨씬 많다는 점을 고려해야 한다. 팀으로 협업하는 환경에서는 다른 사람이 작성한 코드를 읽어야 할 때도 있고, 내가 오래 전에 작성한 코드를 읽을 때도 있다. 코드를 읽어야만 맥락을 이해하고 기존 코드를 수정하거나 새로운 코드를 추가할 수 있다.클린코드를 읽으면 쉽게 코드를 읽고 이해할 수 있기에 중요한 것.기존의 Controller 코드에서는 한 개의 API가 너무 많은 역할을 수행하고 있다.API의 진입 지점으로써 HTTP Body를 객체로 변환현재 유저가 있는지, 없는지 등을 확인하고 예외 처리SQL을 사용해 실제 DB와의 통신 담당기존 Controller에서 하고 있는 역할을 분리하기(리팩토링)UserService 클래스: 현재 유저가 있는지 없는지 등을 확인하고 예외 처리하는 역할을 부여한다.UserRepository 클래스: DB에 SQL을 날리는 역할, 저장장치와의 접근을 담당한다.Layered Architecture: 각 클래스가 각자의 역할을 가지고 겹겹이 쌓인 것 UserRepository 클래스 자체가 JdbcTemplate를 갖도록 해서 파라미터를 통해 매번 넘겨주지 않아도 되게 한다. 진도표에 따라 강의 수강이 이루어지고 있다는 점은 스스로에게 수고했다고 말하고 싶다. 사실 1일차 과제부터 제시간에 제출하지 못했기에 (아직도..) 해결하는 대로 velog와 발자국도 수정할 예정이다.DB는 전공 수업에서 다뤘던 이후로 오랜만에 해 본 건데 API와 연결하는 부분이 아직 손에 익지 않아서 연습을 더 해봐야겠다.Java의 경우도 학교 수업에서 배웠던 거랑 다르게 조금 더 응용된 걸 많이 사용하고 있어서 새롭게 느껴진다. 과제12주차 발자국 작성 전까지 추가 예정 과제2GET API와 POST API를 직접 작성해 보는 실습LocalDate.parse를 이용해서 String을 DayOfWeek로 변환했다. LocalDate가 제공하는 메소드를 이용해 요일을 출력했다.리스트의 합을 구하는 것에 시간이 많이 소요되었는데, Controller에서 Request 클래스에서 return 받은 리스트 객체를 이용해 합을 구할 수 있었다.강의를 보며 그대로 따라하는 것보다 직접 해보려니 더 어렵게 느껴졌던 과제였다. 그래도 직접 적용해 보면서 이해도가 올라가고 있음이 느껴졌다.과제3익명 클래스, 람다 모두 프로그램/코드를 간결하게 한다. 특히 람다는 기존의 메서드 선언에 꼭 필요한 부분이 아닌 것은 생략할 수 있어서 클린하게만 코드를 작성한다면 추후에도 이용하기 좋은 방법 같았다.이외에도 코드의 가독성을 높일 수 있는 여러가지 방법을 공부했는데, 지금까지는 왠지 복잡하고 오히려 에러가 나면 디버깅 하는 것이 귀찮아 잘 쓰지 않았던 것들이었다. 앞으로는 열린 마음으로 스트림 API, 함수형 인터페이스 같은 것들을 사용해 보며 익숙해져야겠다.평소라면 강의 수강을 하면서도 더 알아보거나 공부할 생각을 안 했을텐데, 미션이라는 의무가 부여되었기에 조금 더 능동적으로 공부할 수 있던 일주일이었다. 미션 수행이 조금씩 밀리고 있는데 다음 주는 놓치는 미션 없이 모두 성공해 보겠다.
인프런워밍업클럽스터디