주노 님의 블로그

20240812 본캠프 21일차 TIL 본문

TIL

20240812 본캠프 21일차 TIL

juno0432 2024. 8. 12. 23:24

본캠프 21일차 내용 간단요약

  • 09:00 ~ 10:00 : 코드카타
  • 10:00 ~ 10:10 : 팀 회의
  • 10:10 ~ 11:00 : 회의
    API 명세 보강 및 간단한 질문
  • 11:30 ~ 12:00 : 강의
    1주차 9강
  • 12:00 ~ 13:00 : 점심시간
  • 13:00 ~ 18:00 : 강의
    1주차 10강 ~  1주차 20강
  • 18:00 ~ 19:00 : 저녁시간
  • 19:00 ~ 20:00 : 강의
    1주차 21강 ~ 1주차 24강
  • 20:00 ~ 21:00 : TIL작성 

오늘 해야할 일✔️ 🔺 ❌

✔️ SPRING 1주차 강의

🔺과제 요구사항 1

 

 

예~~ 넘나 힐링이었어! 럭히빅히자낭!

 


코드카타

더보기

덧칠하기 문제

 

프로그래머스

코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.

programmers.co.kr

문제가 너무 길어서 풀기 싫었지만..! 대충 감은 잡혔다

 

class Solution {
        public int solution(int n, int m, int[] section) {
            int answer = 0;
            int [] arr = new int [n];
            for(int i = 0 ; i < section.length; i++)
            {
                arr[section[i]-1] = 1;
            }
            for(int i = 0 ; i < n; i++)
            {
                if(arr[i] == 1)
                {
                    for(int j = i ; j<i+m && j<arr.length; j++)
                    {
                        arr[j]=0;
                    }
                    answer++;
                }
            }
            return answer;
        }
    }

 

플래그 값을 세우고 만약에 색칠하지 않았을경우, 길이만큼 색칠하기 

m과 n이 10만개라 for문을 하나로 줄일 방법을 생각해봤지만

흠.,.. 딱히 생각이 나진 않는다

 

기사단원의 무기

 

프로그래머스

코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.

programmers.co.kr

문제가 복잡하지만 간단히 요약하면

number까지의 약수를 구해서

number의 약수의 개수가 limit를 넘으면 power로

아니면 약수의 개수로 힘을 갖는다

그리고 철의 수는 해당 limit의 수나, power로 결정한다

 

이거였다

 

private int divisor(int num)
    {
        int count=0;
        int sqrt = (int) Math.sqrt(num);
        for(int i = 1; i <= sqrt; i++)
        {
            if(num%i==0)
            {
                count++;
                if(i != num/i)
                    count++;
            }
        }
        return count;
    }

 

일단 약수를 구하는 메서드를 만들고

 

public int solution(int number, int limit, int power) 
    {
        int answer = 0;
        for(int i = 1 ; i <= number; i++)
        {
            int result = divisor(i);
            if(result > limit)
            {
                answer += power;
            }
            else
            {
                answer += result;
            }
        }
        return answer;
    }

그걸 사용해서 값을 구한다

 

먼저 약수의 수를 구하는 메서드를 실행한뒤 메서드의 결과값이 limit를 넘는다면 파워로 고정

아니면 result값으로 고정하면된다

 


강의

더보기
  • Spring MVC
    디자인 패턴 중 하나
    Model-View-Controller의 약자이다

    Model
    데이터와 비즈니스 로직을 담당한다
    데이터베이스와 연동하여 데이터를 저장하고 불러오는 등의 작업을 수행합니다.

    View
    사용자 인터페이스를 담당함
    사용자가 보는 화면과 버튼, 폼 등을 디자인하고 구현합니다.

    Controller
    Model과 View 사이의 상호작용을 조정하고 제어합니다.
    사용자의 입력을 받아 Model에 전달하고 Model의 결과를 바탕으로 View를 업데이트한다

  • servlet
    자바를 사용하여 웹 페이지를 동적으로 생성하는 서버측 프로그램이다
    내일배움캠프 스프링 강의 1주차 9강중.
    사용자가 request를 요청하면 서블릿은 객체를 생성한다
    서블릿은 약속된 규약을 맞추면서 http에 대한 알맞은 request와 response를 만들어 분석한다
    매칭되는 서블릿을 찾아서, service메서드를 호출한뒤, 메서드에 따른 올바른 반환값으로 메스드를 호출한다
    호출한 결과를 httpservletResponse 객체에 응답을 담아 브라우저에 반환하며
    request와 response를 반환해준다.

    매칭이 되는 서블렛을 다 구현해야하나?
    >> 아니다 스프링은 dispatcherServlet을 만들어 편하게 위 과정을 수행할 수 있도록 한다.
    내일배움캠프 스프링 강의 1주차 9강중

    @RestController
    public class HelloController {
        @GetMapping("/api/hello")
        public String hello() {
            return "Hello World!";
        }
    }

    /api/hello 경로로 get요청이 들어오면 아래와같은 메서드를 반환해주는 코드가 있을때

    request요청을 받으면 DispatcherServlet는 해당하는 컨트롤러 (HelloController)에 요청을 보낸다
    해당 컨트롤러는 요청에 대한 처리를 완료한후 데이터와 view정보를 전달한다
    디스패처는 viewResolver를 사용하여 컨트롤러가 반환한 논리적인 뷰를 실제 사용자가 볼 수 있는 물리적인 뷰로 변환합니다. DispatcherServlet은 이 뷰에 모델 데이터를 적용하여 최종 결과를 클라이언트에게 응답으로 전달한다.

  • Controller
    위 api를 만들어보자.
    컨트롤러란? 
    사용자의 웹 브라우저나 클라이언트를 통해서 http 요청을 보내면 디스패처 서블렛에 의해 적절한 컨트롤러로 전달된다.
    /api/hello 경로로 get요청이 들어온다면 해당 요청은 HelloController로 전달이된다.
    컨트롤러는 요청을 처리하기 위해 매핑된 메서드를 실행하며, 그 과정중에서 데이터베이스를 조회하거나, 적절한 응답을 생성한다.
    컨트롤러에서 리턴된 데이터를 디스패처 서블렛은 받아 최종 응답을 보내게된다.

  • 실습
    @Controller
    public class HelloController
    {
        @GetMapping("/api/hello")
        @ResponseBody
        public String hello() {
            return "Hello World";
        }
    }
    


     일단 helloController로 테스트를 해보자.
    상단 클래스에서 컨트롤러 어노테이션을 하게된다면, hello메서드에서 hello world라는 뷰정보를 찾으려고
    resourecs에서 해당 html(helloWorld.html)을 찾게된다
    현재는 그냥 string만 반환하면 되니까, ResponsBody를 하게되면 string값이 보내지게된다.
    postman에서 api를 요청하게된다면 디스패처 서블릿이 작동을 하는것을 확인 할 수 있다.
    이 작업을 수행한다고 생각하면 된다.
    @Controller
    public class HelloController
    {
        @GetMapping("/api/hello")
        @ResponseBody
        public String hello() {
            return "Hello World";
        }
    
        @GetMapping("/api/get")
        @ResponseBody
        public String get() {
            return "Get Method 요청";
        }
    
        @PostMapping("/api/post")
        @ResponseBody
        public String post() {
            return "Post Method 요청";
        }
    
        @PutMapping("/api/put")
        @ResponseBody
        public String put() {
            return "PUT Method 요청";
        }
    
        @DeleteMapping("/api/delete")
        @ResponseBody
        public String delete() {
            return "DELETE Method 요청";
        }
    }
    네가지 메서드를 어노테이션만으로 매핑을 할 수 있다.
    만약 서블렛을 사용했다면? 위처럼 길어질것이다
    @RequestMapping("/api")

    위처럼 @RequestMapping을 하고 주소를 입력하게된다면
    중복되는 주소를 줄여 줄 수 있다.
    @GetMapping("/hello")
    @ResponseBody
    public String hello() {
        return "Hello World!";
    }

    위처럼.

  • 정적페이지
    정적페이지는 그냥 그대로 반환해주는 페이지라고 생각하면 편하다.
    사용자의 입력없이, 그냥 띄워주는페이지
    위 링크에 static에다 hello.html을 작성해보자
    @Controller
    public class HtmlController
    {
        //정적 페이지
        @GetMapping("/static-hello")
        public String hello(){
            return "hello.html";
        }
    }
     그리고 위처럼 구현하면
    http://localhost:8080/static-hello
    방법도 되지만

    정적 페이지는 바꿀데이터 자체가 없기때문에
    http://localhost:8080/hello.html
    위 링크처럼 직접접근을 해야한다.

    애초에 타임리프는 위 static을 찾을수없다. 타임리프 엔진은 templates를 찾기때문에 찾을수 없다고 뜬다

    그래도 한번 컨트롤러를 돌고싶다면 아래의 방식을 사용한다
    @GetMapping("/html/redirect")
    public String htmlStatic(){
        return "redirect:/static-hello";
    }

    리다이렉트를 이용한다.
    위 경로를 타지만, redirect:/ 로 새 경로로 다시 반환한다는뜻이다

    http://localhost:8080/hello/redirect로 요청을 하면
    주소창은 결과적으로
    http://localhost:8080/hello.html
    위처럼 되게된다

    개발자 도구로 살펴보면 request는 http://localhost:8080/hello/redirect로 받았지만,
    응답의 위치는 http://localhost:8080/hello.html인걸 볼 수 있다.

  • 동적 페이지
    @GetMapping("/html/dynamic")
    public String htmlDynamic(Model model)
    {
        visitCount++;
        model.addAttribute("visitCount", visitCount);
        return "hello-visit";
    }

    사용자가 접속할때마다 카운트가 상승하는 페이지를 만들어보자.
    매개변수로 모델을 받아서 api에서 모델을 사용할 수 있게 해준다.

    모델에 값을 넣어줄때는 addAttribute를 사용한다.
    리턴값은 template에 있는 html명

    hello-visit.html을 만들어 아래를 넣어보자
    <div>
        (방문자 수: <span th:text="${visitCount}"></span>)
    </div>

    ${visitCount}는 매핑시 설정했던 attribute와 같은것을 알 수 있다.

    view name 정보를 반환하고, viewResolver에 의 해서 visitCount를 매핑해서 넣어준다
    모델에서의 이름이 visitCount라고 된것을 찾아서 넣어준다.. 라고 생각하면 편함.

    • 데이터를 클라이언트에 반환하는방법?
      서버가 직접 뷰를 반환하는가?
      >> 아니다, 요청에 맞는 정보만 반환한다 에 가까워지고있다.
      요즘은 데이터를 json형식으로 반환하는게 가까워지고있다
      클라이언트에서 최초 리퀘스트를 요청하면 서버는 페이지를 반환해주고
      그 후 클라이언트는 AJAX를 통해서 요청을하면 서버는 JSON형식의 데이터를 반환한다고 할 수 있다.

      사실 json형식은 자바에는 없다.
      json형식처럼 String을 보낼 수 밖에없는것이다.
      @GetMapping("/json/string")
      @ResponseBody
      public String helloStringJson() {
          return "{\"name\":\"Robbie\",\"age\":95}";
      }
       위는 사실 {"name" : "Robbie", "age" : 95}
      처럼 구성되어있다.
      리턴값이 json 형식처럼 보인다
      //{"name" : "Robbie", "age" : 95}
      //Content-Type : text형식 html
      @GetMapping("/json/string")
      @ResponseBody
      public String helloStringJson() {
          return "{\"name\":\"Robbie\",\"age\":95}";
      }
      
      //content-Type : application/json형식
      //왜 위와 다른가?
      @GetMapping("/json/class")
      public Star helloClassJson()
      {
          return new Star("Robbie", 95);
      }


      이번에 반환타입이 객체라면 컨텐츠 타입은 application/json형식인데 왜 다른가?
      객체는 자바스크립트 형식으로 반환을 해야한다.
      스프링은 자동으로 json형식으로 직렬화하여 반환한다.
      스프링에서 직렬화하는 방식은 jackson방식이다.

      string일 경우, 그냥 json 느낌나는 string이 반환된것을 볼 수 있지만,
      객체일경우 json형식 그자체인것을 볼 수 있다.

      @RestController

      controller과 RestController의 차이점은?
      controller + responseBody가 합쳐져 클래스의 모든 메서드에 적용되어
      데이터나 string을 반환하게 하는것이다.

      @GetMapping("/json/string")
      @ResponseBody
      public String helloStringJson() {
          return "{\"name\":\"Robbie\",\"age\":95}";
      }

      위 코드에서

      @GetMapping("/json/string")
      public String helloStringJson() {
          return "{\"name\":\"Robbie\",\"age\":95}";
      }

      위 코드만 사용해도 @responseBody 어노테이션을 적용한것과 같다.

      어떨때 쓰나?
      @Controller : view를 반환해야할때는
      @RestController : 모든 클래스가 view를 반환하지 않고 json형태를 반환해야할때 
  •  JACKSON이란?
    JSON 타입을 오브젝트로, 오브젝트형식을 JSON타입으로 자동으로 처리해준다.

    jackson은 objectMapper 객체를 통해 형 변환을 하게된다.

    object에서 json으로 (writeValueAsString)
    @Test
    @DisplayName("Object To JSON : get Method 필요")
    void test1() throws JsonProcessingException {
        Star star = new Star("Robbie", 95);
    
        ObjectMapper objectMapper = new ObjectMapper(); // Jackson 라이브러리의 ObjectMapper
        String json = objectMapper.writeValueAsString(star);
    
        System.out.println("json = " + json);
    }


    objectMapper의 writeValueAsString메서드를 사용하여 객체를 json으로 변경해준다.
    start객체의 getter이 있어야하는데 jackson에서 속성값을 getter로 찾아와서 사용하기때문에 필수로 정의가 되어있어야한다.

    json에서 object (readValue)
    @Test
    @DisplayName("JSON To Object : 기본 생성자 & (get OR set) Method 필요")
    void test2() throws JsonProcessingException {
        String json = "{\"name\":\"Robbie\",\"age\":95}"; // JSON 타입의 String
    
        ObjectMapper objectMapper = new ObjectMapper(); // Jackson 라이브러리의 ObjectMapper
    
        Star star = objectMapper.readValue(json, Star.class);
        System.out.println("star.getName() = " + star.getName());
    }

    string타입을 json으로 변경하기 위해서는 setter 과 기본생성자가 있어야한다.
    string 타입을 json으로 변경하기위해서 setter로 설정하는것이 필요하기때문이다.

    • 클라이언트에서 서버로 요청을 보낼때 데이터를 같이 보낼 수 있다
      보내는 방법은 아래 두가지가 있다. 

    • Path Variable
      Path Variable 방식은 우리가 보내려는 데이터를 URL경로에 추가할 수 있다.
          // [Request sample]
          // GET http://localhost:8080/hello/request/star/Robbie/age/95
          @GetMapping("/star/{name}/age/{age}")
          @ResponseBody
          public String helloRequestPath(@PathVariable String name, @PathVariable int age) {
              return String.format("Hello, @PathVariable.<br> name = %s, age = %d", name, age);
          }
      우리가 입력한 데이터는 중괄호로 감싼다

      @PathVariable 어노테이션을 통해서 받는다
      @PathVariable string name은 위 경로의 중괄호 name일것이고,
      @PathVariable int age는 위 경로의 중괄호 age이다.

      위 값에 이름과 age를 입력하면

      return String.format("Hello, @PathVariable.<br> name = %s, age = %d", name, age);

      return이 그대로 출력될 수 있는것을 볼 수 있다.

      링크는 위처럼 이름과 age에 본인이 적은 값이 들어간것을 알 수있다.

    • Request Param
      쿼리스트링 방식이라고도 한다
      key와 value쌍으로 데이터를 전송하며 key=value&key=value 이런 형식으로 작동한다.
      // [Request sample]
      // GET http://localhost:8080/hello/request/form/param?name=Robbie&age=95
      @GetMapping("/form/param")
      @ResponseBody
      public String helloGetRequestParam(@RequestParam String name, @RequestParam int age) {
          return String.format("Hello, @RequestParam.<br> name = %s, age = %d", name, age);
      }


      링크또한 key와 value쌍으로 되어있는것을 볼 수 있다.

    • Post방식
          // [Request sample]
          // POST http://localhost:8080/hello/request/form/param
          // Header
          //  Content type: application/x-www-form-urlencoded
          // Body
          //  name=Robbie&age=95
          @PostMapping("/form/param")
          @ResponseBody
          public String helloPostRequestParam(@RequestParam String name, @RequestParam int age) {
              return String.format("Hello, @RequestParam.<br> name = %s, age = %d", name, age);
          }

      <form method="GET" action="/hello/request/form/param">
          <div>
              이름: <input name="name" type="text">
          </div>
          <div>
              나이: <input name="age" type="text">
          </div>
          <button>전송</button>
      </form>


      post는 body를 갖고있다 , body를통해 전송하는 방법도 있고,
      쿼리스트링 방식으로 보낼 수 있다.
      request Param처럼 
      @RequestParam으로 작성하면된다.

      @RequestParam은 생략이 가능하다.


    • 만약 모든 파라미터를 전달하지 않으려면?
      @RequestParam(required = false)
      requestParam어노테이션에서  required =  false로 하면
      그 파라미터를 전달하거나, 전달하지않거나 해도 상관없다,
  • @ModelAttribute
    @PostMapping("/form/model")
    @ResponseBody
    public String helloRequestBodyForm(@ModelAttribute Star star) {
        return String.format("Hello, @ModelAttribute.<br> (name = %s, age = %d) ", star.name, star.age);
    }
     body에 있는 queryString 방식의 데이터도 객체에 매핑해서 가지고 올 수 있다.
    @ModelAttribute가 생략이 가능하다.


    @RequestParam
    @ModelAttribute 등이 생략이 가능하다.
    스프링은 자동으로 파라미터를 판단하여 자동으로 매핑해준다.

  • 모든 내용을 requestParam으로 받아오나?
    @PostMapping("/form/param")
    @ResponseBody
    public String helloPostRequestParam(@RequestParam String name, @RequestParam int age) {
        return String.format("Hello, @RequestParam.<br> name = %s, age = %d", name, age);
    }
     지금은 데이터가 두개가 있으면 괜찮지만 10개를 요청하면 10개의 어노테이션이 필요하다

    과연 이렇게 해야할까?
    // [Request sample]
    // GET http://localhost:8080/hello/request/form/param/model?name=Robbie&age=95
    @GetMapping("/form/param/model")
    @ResponseBody
    public String helloRequestParam(@ModelAttribute Star star) {
        return String.format("Hello, @ModelAttribute.<br> (name = %s, age = %d) ", star.name, star.age);
    }

    데이터가 많이 요청이 된다면 클래스를 따로 만든다음, @ModelAttribute 어노테이션과
    필드 접근자를 사용하면 된다.

    데이터를 넣어줄때는 생성자또는 setter를 사용하기때문에
    전체를 매개변수로 받던가, setter를 사용해야한다
    롬복 사용가능

 


강의 - 메모장 만들기

더보기

메모장 프로젝트

  • 기능 설계
    접속 하자마자 메모 전체 목록 조회하기
    GET API 사용해서 메모 목록 불러오기

    메모 생성하기
    POST API 사용해서 메모 신규 생성하기
    생성된 메모 반환

    메모 변경하기
    PUT API 사용해서 메모 내용 변경하기
    사용자가 클릭한 메모가 DB에 존재하는지 확인하기
    해당 메모 내용 변경

    메모 삭제하기
    DELETE API 사용해서 메모 삭제하기
    사용자가 삭제하려는 메모가 DB에 존재하는지 확인하기
    DB에서 해당 메모 삭제


    api가 4개이다
    url이 같은데?
    메서드 방식이 다르기때문에 문제가 없다!

    변경과 삭제는 중괄호로 되어있어 path variable로 되어있다는걸 알수있다.
    request param이었으면 ? a=b& ~~~ 되어있었을것

  • 기본 설정
    index.html을 static 폴더 안에 넣는다.
    spring은 자동으로 프로그램을 실행하면 static폴더 안에 index.html을 찾기때문에
    http://localhost:8080을 입력해도, index.html로 이동하게 된다.

  • Create 구현하기
    메모 생성하기
    메서드 : post
    url : /api/memos
    return : MemberResponseDto

    DTO가 뭘까?
    데이터 전송 및 이동을 위해 생성되는 객체이다
    클라이언트에서 보내오는 데이터를 객체로 처리할때 사용된다.
    서버에서 클라이언트에 반환할때도 사용된다.

    @Getter
    public class MemoResponseDto {
        private Long id;
        private String username;
        private String contents;
    }

    dto는 위와같이 설계되었다.
    model과 같은거 아닌가? 싶지만
    데이터베이스와 소통하는 model은 직접 수정을 하지 말아야한다.


    private final Map<Long, Memo> memoList = new HashMap<>();
    
        @PostMapping("/memo")
        public MemoResponseDto createMemo(@RequestBody MemoRequestDto requestDto)
        {
            //requestDto -> entity
            Memo memo = new Memo(requestDto);
    
            //memo의 max id를 찾는다
            //id값은 중복이 되면 안되니까 최대값을 찾은후 차곡차곡 쌓아야함
            Long maxId = memoList.size() > 0 ? Collections.max(memoList.keySet()) + 1 : 1;
            memo.setId(maxId);
    
            //DB 저장
            memoList.put(memo.getId(), memo);
    
            //Entity -> ResponseDto
            MemoResponseDto responseDto = new MemoResponseDto(memo);
            
            return responseDto;
        }
    일단 메모를 생성하는 post메서드를 만들자
    지금은 데이터베이스가 없으니까 map에 넣는다고 가정하자.

    @RequestBody 어노테이션을 사용하며 requestDto를 매개변수로 받는다.
    사용자에게 입력받은 값을 requestDto를 이용해 메모 객체를 생성하자

    requestDto 생성하기
    @Getter
    public class MemoRequestDto {
        private String username;
        private String contents;
    }

    public class Memo {
        private Long id;
        private String username;
        private String contents;
    
        public Memo(MemoRequestDto requestDto)
        {
            this.username = requestDto.getUsername();
            this.contents = requestDto.getContents();
        }
    }
     requestDto를 만들어서
    model에 생성자를 만든다.
    username에 requestDto의 username을 받아오고, contents에 requestDto의 contents를 받아와서
    새로운 객체를 만들어준다.

    그다음 데이터베이스가 없기때문에 자동으로 id값을 증가시키는 연산을 한다
    삼항연산자를 이용하며 맵의 사이즈가 0보다 크면 ? 맵의 모든 키 리스트를 가져와서 큰 값을 +1하거나 : 1을 반환한다
    이다.

    그다음 모든 id값, name값, contents값이 저장이 되었다면, map(데이터베이스)에 저장을 한 후
    응답용 dto를 만든다.
    package com.sparta.springprepare.dto;
    
    import com.sparta.springprepare.entity.Memo;
    import lombok.Getter;
    
    @Getter
    public class MemoResponseDto {
        private Long id;
        private String username;
        private String contents;
    
        public MemoResponseDto(Memo memo)
        {
            this.id = memo.getId();
            this.username = memo.getUsername();
            this.contents = memo.getContents();
        }
    }

    응답용 dto는 memo객체에서 id값, username값, content값을 가져와 반환하는 역할을 한다.


  • Read 구현
    @GetMapping("/memos")
    public List<MemoResponseDto> getMemos() {
        // Map To List
        List<MemoResponseDto> responseList = memoList.values().stream()
                .map(MemoResponseDto::new).toList();
    
        return responseList;
    }
    stream을 사용해 모든 메모를 가져온다


  • 진행상황


    메모가 정상적으로 등록 된 것을 볼 수 있고, 메모 조회까지 가능한것을 볼 수 있다


    물론 poset man으로도 post할 수 있다
    body > raw > json으로 바꾼후, 작성하면된다!


  • update
    @PutMapping("/memos/{id}")
    public Long updateMemo(@PathVariable Long id, @RequestBody MemoRequestDto requestDto){
        //메모가 실제로 데이터베이스에 존재하는지 확인해보기
        if(memoList.containsKey(id)){
            //해당 메모 객체 가져오기
            Memo memo = memoList.get(id);
    
            //해당 메모 수정하기
            memo.update(requestDto);
            return memo.getId();
        }
        else {
            throw new IllegalArgumentException("선택한 메모는 존재하지 않습니다");
        }
    }

    업데이트는 put으로 구현했다
    {}니까 PathVariable로 데이터를 받아와야한다
    데이터를 수정해야 한다는뜻은 body에서 json형식으로 받아올것이다. @RequestBody로 해당하는 데이터를 받아온다.
    public void update(MemoRequestDto requestDto)
    {
        this.contents = requestDto.getContents();
        this.username = requestDto.getUsername();
    }

    수정된 내용을 받아와서 저장해준다.
  • delete
    @DeleteMapping("/memos/{id}")
    public Long deleteMemo(@PathVariable Long id){
        //해당 메모가 데이터베이스에 존재하는지 확인
        if(memoList.containsKey(id)){
            memoList.remove(id);
            return id;
        }
        else
        {
            throw new IllegalArgumentException("선택한 메모는 존재하지 않습니다");
        }
    }

    현재는 데이터베이스를 통하지도 않고 map에서 삭제를 하면되서 remove를 사용했다.

DBMS

더보기
  • 데이터베이스
    여러 데이터의 집합

  • DBMS
    Database Management System

  • RDBMS
    Relational DBMS의 약자로 관계형 데이터베이스라고 불린다.

    RDBMS는 테이블 이라는 최소 단위로 구성되며, 테이블은 열 과 행으로 이루어져있다.

    테이블 간 외래 키를 통해 다른 데이터를 조합해서 함께 볼 수 있다라는 장점이 있다.

    RDBMS에는 MySql 등이 있다.

  • SQL
    관계형 데이터베이스에서 사용되는 언어이다, 데이터베이스 내에서 조회 수정 등의 데이터베이스 접근 작업을 할때 SQL 언어를 사용해야한다.

  • DDL : 테이블이나 관계의 구조를 생성하는데 사용된다
    CREATE : 새로운 데이터베이스 및 테이블을 생성해준다
    ALTER : 데이터베이스와 테이블의 내용을 수정 할 수 있다.
    DROP : 데이터베이스와 테이블을 삭제 할 수 있다
    TRUNCATE : 데이터베이스와 테이블을 삭제 할 수 있다. 최초 테이블이 만들어졌던 상태 컬럼값만 남긴다

  • DCL :: 데이터의 사용권한을 관리하는데 사용한다.
    GRANT : 사용자 또는 ROLE에 대해 권한을 부여할 수 있다
    REVOKE : 사용자 또는 ROLE에 부여한 권한을 회수 할 수 있다

  • DML : 테이블에 데이터를 검색, 삽입, 수정, 삭제하는데 사용
    INSERT : 테이블에 새로운 ROW를 추가 할 수 있다.
    SELECT : 테이블의 ROW를 선택 할 수 있다
    UPDATE : 테이블의 ROW의 내용을 수정 할 수 있다
    DELETE : 테이블의 ROW를 삭제 할 수 있다.

프로젝트와 MYSQL의 연동


  • 좌측 데이터베이스 클릭


    Mysql 클릭
     

    기존에 등록해놓은 database와 연동후, testConnection 클릭,

    db창에서 재생느낌나는 아이콘을 클릭하면 쿼리를 작성할수있는 콘솔이있다.

  • CREATE : 테이블 생성
    CREATE TABLE IF NOT EXISTS STUDENT
    (
    	student_code varchar(100) primary key comment '수강생코드', 
    	name varchar(100) not null comment '이름',
    	birth varchar(8) null comment '생년월일',
    	gender varchar(1) not null comment '성별',
    	phone varchar(11) null comment '전화번호',
    	major_code varchar(100) not null comment '주특기코드',
    	foreign key(major_code) references major(major_code)
    );

    majo테이블의 majorcode를 외래키로 가져오겠다 라는뜻으로
    현재 질의는 테이블을 생성하는 질의이다.

  • ALTER : 기본키, 외래키 설정
    ALTER TABLE EXAM ADD PRIMARY KEY(student_code, exam_seq);
    ALTER TABLE EXAM ADD CONSTRAINT exam_fk_student_code FOREIGN KEY(student_code) REFERENCES STUDENT(student_code);
     
    테이블의 기본키를 설정하고
    외래키를 설정한다 exam_fk_student_code는 제약조건의 이름 아무렇게나 지으면됨,
    exam의 테이블의 student_code를 student테이블의 sutdent_code를 참조한다

  •  INSERT : 데이터 추가
    INSERT INTO MAJOR VALUES('m1', '스프링', '남병관');
    INSERT INTO MAJOR VALUES('m2', '노드', '강승현');
     INSERT INTO ~ VALUES를 사용해 입력할 수 있다.
    INSERT INTO STUDENT(student_code, name, gender, major_code) VALUES('s12', '권오빈', 'M', 'm3');

    또는 위와같이 원하는 값만 넣고싶을때는 위와 같이 하면된다.

  • UPDATE : 수정
    UPDATE STUDENT SET major_code= 'm2' where student_code= 's0';

    UPDATE 테이블 SET 바꿀값 WHERE 조건;

  • DELETE : 삭제
    DELETE FROM STUDENT WHERE student_code = 's0';

    DELETE FROM 테이블 WHERE 조건


  • SELECT : 조회
    SELECT * FROM STUDENT;
    > STUDENT 테이블의 전체 값을 받아옴
    SELECT * FROM STUDENT WHERE STUDENT_CODE = 's1';
    > STUDENT테이블의 STUDENT_CODE가 S1인 데이터의 모든 정보를 가져옴
    SELECT name, major_code FROM STUDENT WHERE student_code = 's1';
    >STUDENT테이블의 STUDENT_COD가 S1인 데이터의 NAME, MAJOR_CODE를 가져옴


  • JOIN : 여러 테이블을 연결
    SELECT s.name, s.major_code, m.major_name FROM STUDENT s JOIN MAJOR m ON s.major_code = m.major_code;
    STUDENT의 테이블과, MAJOR의 테이블을 결합하여 데이터를 조회한다.

강의 - jdbc템플릿을 사용하여 mysql 데이터베이스 접근하기.

더보기
  • JDBC란?
    만약 MYSQL을 사용하다가, 다른 RDBMS로 바꾼다면?
    질의를 다시 작성해야할 수도 있다

    위의 문제를 해결하기 위해 JDBC표준이 등장하였다

    자바에서 DB를 연결하기위해서는 자바의 언어로 작성할 수 있는 API이다

    다른 RDBMS로 교체할때는 드라이버만 교체해주면 적용이 된다


  • JDBCTemplate란?
    JDBC의 불필요한 로직 (준비 및 실행, 종료 등등)을 대신 처리해준다.

  • DB연동방법

    application.properties에서 설정을 준다.
    spring.datasource.url=jdbc:mysql://localhost:3306/memo
    spring.datasource.username=root
    spring.datasource.password={비밀번호}
    spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver


  • jdbc템플릿을 사용한 memoController 사용법
    private final JdbcTemplate jdbcTemplate;
    jdbc템플릿을 사용하기위해  선언
    public MemoController(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }
    생성자 주입을 통해 memocontroller에서 jdbc템플릿을 사용하게 해준다.
    @PostMapping("/memos")
    public MemoResponseDto createMemo(@RequestBody MemoRequestDto requestDto) {
        // RequestDto -> Entity
        Memo memo = new Memo(requestDto);
    
        // DB 저장
        KeyHolder keyHolder = new GeneratedKeyHolder(); // 기본 키를 반환받기 위한 객체
    
        String sql = "INSERT INTO memo (username, contents) VALUES (?, ?)";
        jdbcTemplate.update( con -> {
                    PreparedStatement preparedStatement = con.prepareStatement(sql,
                            Statement.RETURN_GENERATED_KEYS);
    
                    preparedStatement.setString(1, memo.getUsername());
                    preparedStatement.setString(2, memo.getContents());
                    return preparedStatement;
                },
                keyHolder);
    
        // DB Insert 후 받아온 기본키 확인
        Long id = keyHolder.getKey().longValue();
        memo.setId(id);
    
        // Entity -> ResponseDto
        MemoResponseDto memoResponseDto = new MemoResponseDto(memo);
    
        return memoResponseDto;
    }

    등록에서 보면 insert into memo (username, contents) << 옆에 들어갈 내용이 values 뒤의 ? ?가 있다
    ? ? 는 preparedStatement.setString으로 삽입해주는데 (1, memo.getUsername())은 1번째 ?에 username을 get으로 가져온다음 넣는다 라고 생각하면된다.
    @GetMapping("/memos")
    public List<MemoResponseDto> getMemos() {
        // DB 조회
        String sql = "SELECT * FROM memo";
    
        return jdbcTemplate.query(sql, new RowMapper<MemoResponseDto>() {
            @Override
            public MemoResponseDto mapRow(ResultSet rs, int rowNum) throws SQLException {
                // SQL 의 결과로 받아온 Memo 데이터들을 MemoResponseDto 타입으로 변환해줄 메서드
                Long id = rs.getLong("id");
                String username = rs.getString("username");
                String contents = rs.getString("contents");
                return new MemoResponseDto(id, username, contents);
            }
        });
    }

    db조회는 query메서드를 사용하는데, 받은 반환값인 resultset을 memoresponseDto로 변환해주기위해
    rowmapper를 사용한다. 배열을 돌면서 값을 모두채워간다고 생각하면된다.

    @PutMapping("/memos/{id}")
    public Long updateMemo(@PathVariable Long id, @RequestBody MemoRequestDto requestDto) {
        // 해당 메모가 DB에 존재하는지 확인
        Memo memo = findById(id);
        if(memo != null) {
            // memo 내용 수정
            String sql = "UPDATE memo SET username = ?, contents = ? WHERE id = ?";
            jdbcTemplate.update(sql, requestDto.getUsername(), requestDto.getContents(), id);
    
            return id;
        } else {
            throw new IllegalArgumentException("선택한 메모는 존재하지 않습니다.");
        }
    }
    update 메서드를 사용하면된다.
    @DeleteMapping("/memos/{id}")
    public Long deleteMemo(@PathVariable Long id) {
        // 해당 메모가 DB에 존재하는지 확인
        Memo memo = findById(id);
        if(memo != null) {
            // memo 삭제
            String sql = "DELETE FROM memo WHERE id = ?";
            jdbcTemplate.update(sql, id);
    
            return id;
        } else {
            throw new IllegalArgumentException("선택한 메모는 존재하지 않습니다.");
        }
    }
    update메서드를 사용하여 메모를 삭제한다.
    private Memo findById(Long id) {
        // DB 조회
        String sql = "SELECT * FROM memo WHERE id = ?";
    
        return jdbcTemplate.query(sql, resultSet -> {
            if(resultSet.next()) {
                Memo memo = new Memo();
                memo.setUsername(resultSet.getString("username"));
                memo.setContents(resultSet.getString("contents"));
                return memo;
            } else {
                return null;
            }
        }, id);
    }
    findbyid는 db에 해당하는 id값을 찾는다. 라는 뜻이다
    query메서드를 사용해서, 특정 id를 가진 메모를 찾는다

    요약하면 조회에는 query메서드를
    나머지 기능에는 update메서드를 사용한다.

'TIL' 카테고리의 다른 글

20240814 본캠프 23일차 TIL  (0) 2024.08.14
20240813 본캠프 22일차 TIL  (0) 2024.08.14
20240809 본캠프 20일차 TIL  (0) 2024.08.09
20240808 본캠프 19일차 TIL  (0) 2024.08.08
20240807 본캠프 18일차 TIL  (0) 2024.08.07