서버와 스프링 부트 시작하기 1주차

서버와 스프링 부트 시작하기 1주차

강의 출처 : https://inf.run/Hywa

 

최태현 강사님의 자바와 스프링 부트로 생애 최초 서버 만들기, 누구나 쉽게 개발부터 배포까지! [서버 개발 올인원 패키지]

 

1주차(1강부터 18강) 워밍업을 정리해보았습니다.

 

1주차 학습 학습 목표

  1. 스프링 부트 프로젝트를 설정하고 시작하는 방법

  2. 서버란 무엇인지, 네트워크와 HTTP, API는 무엇인지, JSON은 무엇인지 등 서버 개발에 필요한 다양한 개념을 이해한다.

  3. 스프링 부트를 이용해 간단한 GET/POST API를 만든다.

  4. 디스크와 메모리의 차이를 이해하고 Database의 필요성을 이해한다.

  5. MySQL Database를 SQL과 함께 조작할 수 있다.

  6. 스프링 서버를 이용해 Database에 접근하고 데이터를 조회,저장,수정,삭제할 수 있다.

  7. API의 예외 상황을 알아보고 예외를 처리할 수 있다.

  8. 좋은 코드가 왜 중요한지 이해하기

 

스프링 부트 프로젝트를 설정하고 시작하는 방법

  1. 기존 프로젝트 파일을 IDE를 통해서 build.gradle을 선택하여 시작합니다.

  2. 스프링 https://start.spring.io/로 들어가서 스프링 환경 설정과 의존성을 추가하여 생성한뒤 1번 방식으로 실행한다.

     


    서버란 무엇인지 서버 개발에 필요한 다양한 개념을 이해한다.

    서버는 serve + er 를 말하며 제공하다 단어와 er이 합쳐져 " 제공하는 것"을 의미합니다.

     

    제공한다는 의미는 목적어가 붙게 되면, 기능을 제공하는 것을 서버라고 합니다.

     

    이라는 건 물건을 의미하고, 웹 서비스를 제공하기 위한 서버에서는 컴퓨터를 말합니다.

서버가 기능을 제공하려면 누군가 원하는 기능을 요청을 해야 기능을 제공할 수 있습니다.

요청하는 역할을 우리는 클라이언트라고 하며, 클라이언트 - 서버 관계는 요청-응답 관계로 이루어지게됩니다.

 

서버와 클라이언트가 서로 요청과 응답을 하기 위해서 통신 매개체가 필요한데 우리는 인터넷이라는 네트워크를 사용합니다.

 

인터넷을 통해서 요구사항을 서버에 전송을 하려면, 서버에 위치를 알아야합니다.

인터넷 네트워크 환경에서 서버(컴퓨터)는 인터넷 회선마다 고유번호를 갖게되며 그걸 주소로 사용합니다.

우리는 그걸 IP라고 합니다.

 

인터넷이 연결된 서버는 IP를 통해서 접근할 수 있으며, 기능을 제공하는 역할은 컴퓨터가 실행중인

프로세스에게 요청을 해야하며, 프로세스는 고유의 번호인 포트를 받아 사용합니다.

그래서 클라이언트가 특정 위치에 있는 컴퓨터가 실행하고 있는 특정 기능을 제공하는 프로세스에 접근하려면

IP와 PORT 번호를 알고 있어야 접근하고 요청할 수 있습니다.

 

HTTP

인터넷 네트워크 환경에서 클라이언트와 서버가 안전하고 빠르게 요청과 응답을 하기 위해서 규칙이 필요한데

그중 한가지 방법을 HTTP라고 합니다.

요청 HTTP와 응답 HTTP는 차이가 있습니다.

요청 HTTP는 HTTP Method,Path,query 또는 body 를 통해서 데이터를 전송하고

응답 HTTP는 HTTP Status,body를 통해서 요청에 대한 응답을 전송합니다.

 

API

API는 클라이언트와 서버가 원하는 기능을 제공하고 응답하기 위해 규칙을 정의합니다.

서버가 정해진 기능을 제공하기 위해 인터페이스를 만들고,

클라이언트는 해당 인터페이스 규격에 맞게 데이터를 전송하도록 하는것을 말합니다.

이러한 규칙은 데이터를 교환하고 상호작용 하는데 필요한 명세서로 서버와 클라이언트 간의

통신을 표준화하여 서로 다른 시스템 간의 통합을 용의하게 합니다.

 

스프링 부트를 이용해 간단한 GET/POST API를 만든다.

  • GET

  @GetMapping("/add") // HTTP Method
    public int addTwoNumbers(@ModelAttribute CalculatorAddRequest request) {
        return request.getNumber1()+ request.getNumber2();
    }
  • POST


    @PostMapping("/multiply") // HTTP Method
    public int multiplyNumbers(@RequestBody CalculatorMultiplyRequest request) {
        return request.getNum1() * request.getNum2();
    }

요청 HTTP를 만들 때 HTTP Method가 꼭 필요하다고 했습니다.

HTTP Method는 클라이언트가 원하는 동작을 서버에게 전송하기 위해 필요합니다.

GET = 조회, POST = 등록 처럼 서버에게 원하는 기능을 전달하기 위해 필요합니다.

 

디스크와 메모리의 차이를 이해하고 Database의 필요성을 이해한다.

클라이언트가 데이터 저장을 요청하여 저장했습니다.

private final List<User> memory = new ArrayList<>();

해당 방식으로 저장할 경우, 서버가 재시작되는 상황이 되면 기존에 있는 데이터는 사라집니다.

 

서버를 재시작하면 기존 데이터가 초기화가 되는 이유는 컴퓨터 구조때문입니다.

컴퓨터는 CPU, RAM, SSD/HDD 하드웨어 구조로 되어있습니다.

CPU는 연산을 하고, RAM은 CPU가 연산을 할때 필요한 데이터를 임시 보관하는 역할을 하며,

SSD,HDD는 연산된 결과를 반 영구 저장하기 위해 필요합니다.

 

현재 방식은 데이터를 RAM에 저장하기 때문에 서버를 재시작하면 저장된 데이터는 사라집니다.

 

그래서 데이터를 반영구 저장하는 SSD나 HDD에 데이터를 저장해야하는데

운영체제가 제공하는 기능을 프로그래밍 언어를 통해서 저장할 수 있지만,

사용자가 원하는 구조로 데이터를 저장하고, 수정하기 위해서 데이터베이스 라는걸 사용합니다.

 

MySQL Database를 SQL과 함께 조작할 수 있다.

SQL에 대한 내용은 생략하겠습니다.

MySQL에는 database라는 구조가 있습니다. 자바로 치면 최상위 패키지로 데이터를 저장할때 구분할 수 있는 폴더라고 생각하면 됩니다.

 

스프링 서버를 이용해 Database에 접근하고 데이터를 조회,저장,수정,삭제할 수 있다.

자바 진영에서 데이터베이스에 접근하는 기술은 JDBC를 사용합니다.

JDBC를 구체화한 클래스인 JDBCTemplate을 사용하여 데이터베이스를 사용할 수 있습니다.

    @PostMapping("/user")
    public void saveUser(@RequestBody UserCreateRequest request) {
//        users.add(new User(request.getName(), request.getAge()));
        String sql = "INSERT INTO user (name,age) VALUES (?,?)";
        jdbcTemplate.update(sql, request.getName(), request.getAge());
    }

API의 예외 상황을 알아보고 예외를 처리할 수 있다.

클라이언트가 요청한 기능과 필요한 데이터를 서버에서 검증을 하고 올바르지 않은 데이터가 들어온다면

클라이언트에게 오류를 전달해야합니다.

    @PostMapping("/user")
    public void saveUser(@RequestBody UserCreateRequest request) {
         if (request.getAge() < 0) {
            throw new IllegalArgumentException("나이는 0살 이상만 가능합니다.");
        }
        String sql = "INSERT INTO user (name,age) VALUES (?,?)";
        jdbcTemplate.update(sql, request.getName(), request.getAge());
    }

 

좋은 코드가 왜 중요한지 이해하기

백엔드 개발자는 클라이언트가 원하는 기능을 제공하기 위해 프로그래밍 언어를 통해서

요구사항을 코드로 작성하는 역할을 합니다.

 

기술적인 요구사항이건, 비즈니스 요구사항 이건 결국 어떤 요구사항을 수행하기 위한 기능을 동작하기 위해서 코딩을 하게됩니다.

 

코드를 작성하는 일을 하지만, 코드를 작성하는 시간보다 코드를 읽는 시간이 더 많습니다.

그리고 혼자서 일하는 개발자보다 팀으로 개발을 하게됩니다.

 

요구사항을 기능으로 동작하기 위해 작성된 코드를 다른 사람이 봐도 어떤 요구사항을 코드로 작성했는지 알 수 있어야합니다.

 

기존에 작성된 전체 코드를 확인해보면

    @PostMapping("/user")
    public void saveUser(@RequestBody UserCreateRequest request) {
//        users.add(new User(request.getName(), request.getAge()));
        if (request.getAge() < 0) {
            throw new IllegalArgumentException("나이는 0살 이상만 가능합니다.");
        }
        String sql = "INSERT INTO user (name,age) VALUES (?,?)";
        jdbcTemplate.update(sql, request.getName(), request.getAge());
    }

지금은 간단한 코드이지만, 수천줄이 된다면 해당 컨트롤러 기능인 saveUser를 수정하기 위해서는

첫번째 줄부터 마지막 줄까지 읽어야합니다.

 

  1. 새로운 사람이 들어오거나 시간이 지나서 작성된 코드를 이해하기 위해서 소요되는 시간이 점점 늘어납니다.

  2. 함수의 어느 부분을 수정할 경우, 전체 코드중 어디까지 영향을 미칠지 모릅니다.

  3. 여러명이서 하나의 컨트롤러를 수정하기 위한 리소스가 많이 필요합니다.

  4. 너무 큰 하나의 기능이 되어 테스트하기 어렵습니다.

  5. 1~4번까지 맞물려 유지보수성이 떨어집니다.

따라서 최대한 클린코드로 작성해야 생산성이 높아지게 됩니다.

 

1주차 간단 회고

코드를 작성할때 우선 순위를 정하게 되었습니다.
회사에서 저에게 주는 프로젝트는 프론트부터 백엔드까지 제가 만들기 때문에 시간에 쫒겨서 돌아기만 하는 코드 덩어리가 되었습니다.

1주차 강의를 보고 직전에 작성된 프로젝트를 보니 돌아가기는 하는데 해당 함수가 무슨 기능을 하는지 알 수가 없어서

처음부터 끝까지 코드를 읽어야했습니다.

강사님이 말씀하신 코드는 요구사항을 표현하는 언어다 , 그리고 다른 사람이 코드를 봤을 때 요구사항을 한 눈에 파악할 수 있어야 한다는 걸 보고 반성을 많이 했습니다.

 

클린코드가 변수명, 하나의 기능, 하나의 책임은 알고 있어서 최대한 지키려고 했지만 이걸 하는 이유를 이해하지 못해서

무작정 변수명 길게하고, 하나의 기능으로 나누고 , 하나의 책임으로 나누기만 하다보니 오히려 코드가 복잡해지고

유지보수하기가 어려웠습니다.

 

앞으로 학습을 할 때도 새로나온 라이브러리니까, 빠르다고 하니까 , 좋다고 하니까 사용해야지라는 생각보다

이걸 왜 사용하는지에 대한 이해를 먼저 하도록 하겠습니다.

 

2주차 학습 방법에 대한 목표

2주차는 스프링 컨테이너와 트랜잭션 , JPA에 대한 강의인데

이미 알고 있는 내용이지만, 왜 사용하는지에 대해 초점을 맞춰서 학습하도록 하겠습니다.

 

미션

1일차

:https://kamser0415.tistory.com/7

  • 어노테이션을 사용하는 이유 (효과) 는 무엇일까?

  • 나만의 어노테이션은 어떻게 만들 수 있을까?

     

    어노테이션을 사용하는 이유를 찾기위해서 먼저 믿을 수 있는 공식 문서와 토비님의 강의를 참고했습니다.


    블로그에도 글이 많지만 공식 문서에서 작성된 내용은 만든 사람의 목적을 확인할 수 있다고 생각했고,
    토비님의 강의를 참고한 이유는 실무에서 오래 사용하신 개발자 분의 생각을 확인 하는 방법이라 생각했습니다.

2일차

: https://kamser0415.tistory.com/8

  • 간단한 GET 요청과 응답, POST 요청에 대한 문제입니다.

주어진 두 수를 컨트롤어에서 받아서 덧셈,뺄셈,곱샙에 대한 결과를 응답의 결과로 내려주는게 문제입니다.

  1. 연산만 책임지는 클래스를 만들어보자.

  2. 연산의 결과를 반환하는 용도의 클래스를 만들자.

연산만 책임지는 클래스를 만든 이유는 DTO에 연산 로직을 넣을까 생각을 했지만,

단순한 값이 있다 없다, 특정 조건에 만족하는지 확인하는 것과 다른 연산이라는 로직이 들어가면

다른 컨트롤러에서 사용하게 된다면 해당 로직이 필요하지 않을 수 있고, 거기서도 연산이 필요한데 연산 방식이 조금 차이가 있다면 그 DTO에 또 추가가 되면 DTO의 책임이 점점 커질수 있다고 생각했습니다.

그래서 별도의 연산을 하는 클래스를 만드는게 좋다고 생각하여 만들게 되었습니다.

 

연산 클래스는 두 수를 인수로 받아 생성하고, 함수를 호출하면 받은 인수로 연산 결과를 반환합니다.

여기서 실수한게 만약 요구사항이 변경되어 덧셈한 결과를 가지고 곱셈을 해달라고 한다면

새로운 연산 클래스를 만들어야합니다.

 

그래서 다시 한다면 함수마다 연산할 인수를 전달받아 결과를 반환하는 순수 함수로 만들겠습니다.

3일차

:https://kamser0415.tistory.com/9

  • 람다가 등장한 이유

  • 람다와 익명 클래스 관계

  • 람다 간단한 사용 방법

람다가 등장한 이유를 작성하기 위해서

  1. 익명 클래스가 등장한 이유

  2. 익명 클래스의 단점

1,2번을 먼저 찾아봤습니다. 익명 클래스를 보완하기 위해서 람다가 등장했다는건 알고 있었습니다.

처음에는 익명 클래스와 람다의 관계는 제대로 찾아보기 전에는 익명 클래스 내에 람다가 포함된줄 알았습니다.

자바는 1급 객체가 클래스이기 때문에 함수가 별도로 사용할 수 없기 때문에 결국 람다는 추상 메서드가 한개인

함수형 인터페이스를 익명클래스 방식에서 람다 표현식으로 만든거라 생각을 했습니다.

 

아직도 동일한 생각이지만, 함수형 인터페이스와 순수 함수에 대해서 한번 더 생각하게 되는 계기가 되었습니다.

 

4일차

:https://kamser0415.tistory.com/10

  • API를 통해서 과일 정보를 받아 저장한다.

  • API를 통해서 판매된 과일 정보를 받고 결과를 반환한다.

  • API를 통해서 받은 과일 이름으로 판매 완료금액과 판매되지 않은 금액의 합계를 반환한다.

강사님께서 말씀하신 요청 HTTP를 서버에서 받기 위해 API 명세서를 확인하고

HTTP Method,Path,Body or Query에 맞는 컨트롤러 메서드를 만들고 매핑했습니다.

 

API에서 숫자를 표현하는 방법은 int와 long이 있는데 long을 사용한 이유를 찾아봤습니다.

제 생각에는 21억까지 표현할 수 있는 int 타입도 충분히 모든 과일의 금액을 표현할 수 있다고 생각을 했습니다.

 

검색을 해보니 가장 와닿은 말은 호환성이더라구요

현재 데이터를 저장할 때 지금 스프링 부트를 사용하고 있지만, 다른 프레임워크나 프로그래밍언 언어에서도 사용하게 된다면 long이 더 적합하다는 것을 알게되었습니다.

 

회사마다 다르겠지만, 확장될 가능성이나 다른 프레임워크로 넘어가는 환경이 아니라면 int를 사용하는것도 나쁘지 않다고 생각됩니다.

 

5일차

:https://kamser0415.tistory.com/11

 

클린 코드에 대해서 고민을 했습니다.

전체 로직을 보면 주사위 크기는 6 이라고 지정되어있고, 밑에 추가 질문은 변경될 수 있다고 했습니다.

 

변수명 변경하기

  1. 먼저 변수의 이름을 변경해서 해당 변수에는 어떤 데이터가 들어가는지 알 수 있도록 변경했습니다.

각 기능을 함수 단위로 나누기

  1. 주사위 보드판을 만드는 행위

  2. 주사위를 던지는 행위

  3. 주사위의 무작위 숫자가 나오는 행위

  4. 나온 주사위 숫자를 기록하는 행위

각 기능을 나누어보니 이걸 따로 따로 하는 것보다 주사위에 대한 놀이를 하나의 책임으로 생각하고 클래스로 만들면

코드를 보기에 더 간편할거같다는 생각을 했습니다

 

주사위 머신 클래스 만들기

그리고 주사위 머신에서 모든 일을 하는게 아니라 주사위 머신에 주사위 크기와 던지는 횟수를 입력하면

내부에서 알아서 주사위를 만들고 주사위 기록을 저장하는 보드판을 만드는 책임을 가지게 합니다.

 

주사위를 던지는건 주사위 스스로 하도록 지정하여 숫자가 나오는건 주사위 클래스에게 책임을 위임했습니다.

 

코드로 작성할 때는 잘 작성했다고 생각을 했습니다.

 

이걸 글로 작성하다보니 책임을 어디까지 줄 것인지에 대해 한 번더 생각을 해보게 되었습니다.

  1. 주사위 머신은 주사위를 만들고, 주사위 보드판을 만든다.

  2. 주사위 머신은 주사위를 돌릴수 있는 기능이 있다.

  3. 주사위를 돌리는건 주사위 객체가 한다.

  4. 주사위 보드판은 주사위가 돌려진 숫자를 기록하는 기능이 있다.

이렇게 코드를 작성하는게 더 나았을 거라는 생각이 들었습니다.

 

미션 회고

지금까지 회사 일을 하면서 기능에 대해 집중하다보니 간단한 기능이라도 어디서 작업을 할지 고민을 하지 않고

관습처럼 작성하는 습관이 있었습니다.

 

4일차때부터 강사님이 말씀해준 내용을 회사 프로젝트에 적용해보니 제가 생각하기에 코드를 좀 더 요구사항이 코드로 한 눈에 보이게 작성하려고 노력하였고,

5일차때는 단순한 코드지만 리팩토링을 하면서 코드 작성하는게 재밌다는걸 느꼈습니다.

댓글을 작성해보세요.