1. MySQL 설치를 완료한다.


최초 설치 시 root계정으로 비밀번호 설정을 하게 될 것이고

MySQL WorkBench로 root계정으로 로그인 되는지 테스트한다.



MySQL WorkBench로 들어와 test라는 스키마를 만든다.




2. IntelliJ 에서 build.gradle파일과 application.properties 파일을 수정한다.




build.gradle파일의 dependencies를 다음과 같이 작성한다.


1
2
3
4
5
6
7
dependencies {
    compile('org.springframework.boot:spring-boot-starter-data-jpa')
    compile('mysql:mysql-connector-java')
    compile('org.projectlombok:lombok')
    implementation 'org.springframework.boot:spring-boot-starter-web'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
cs


application.properties의 파일은 다음과 같이 작성한다.


1
2
3
4
5
6
7
8
9
10
11
# db url
spring.datasource.url=jdbc:mysql://localhost:3306/test?useSSL=false&useUnicode=true&serverTimezone=Asia/Seoul
 
# db name
spring.datasource.username=root
 
# db password
spring.datasource.password=0000
 
# query print
spring.jpa.show-sql=true
cs


아까전에 MySQL WorkBench로 테스트한 root 계정과 비밀번호를 넣어주고 

DB접속정보 포트(3306)와 연결할 스키마명(test)을 넣어준다.


만약 Illegal char <:>  mysql:mysql-con과 같은 오류가 발생하면 build > build project를 시도해본다.


아래와 같이 프로젝트를 실행시켰을 때 오류가 나지 않는다면 성공이다.





우리가 객체를 만들때 변수를 선언하고 기본생성자부터 Getter, Setter까지 메소드를 다 작성해줘야 하지만

Lombok 플러그인은 기본생성자부터, Getter, Setter 생성을 한번에 해결해준다.

개발자는 객체에 사용될 변수만 선언해주고 클래스명 위에 @Data라는 어노테이션만 선언해주면 된다.


1. File -> Settings 로 진입한다.




2. Plugins > Marketplace > lombok 검색한다.



install 버튼을 누르고 설치를 진행하고 intellij를 restart한다.


3. 설치가 완료되면 build.gradle의 dependencies를 수정한다.


 


1
2
3
4
5
6
dependencies {
    compileOnly 'org.projectlombok:lombok'
    annotationProcessor 'org.projectlombok:lombok'
    implementation 'org.springframework.boot:spring-boot-starter-web'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
cs




build.gradle 마우스 오른쪽 버튼 > Import Gradle Project 선택



build.gradle 에서 dependencies 부분 Run build, Rebuild 한다.



4. 모델로 돌아와 @Data를 선언해주고 lombok을 import한다.


1
2
3
4
5
6
7
8
9
10
package com.example.test.model;
 
import lombok.Data;
 
@Data
public class SearchVO {
    private String id;
    private String email;
    private int page;
}
cs


Structure를 보면 생성자, 메소드가 정의된 것을 확인 할 수 있다.



5. File -> Settings -> Annotation Processors 로 진입한다.



Enable annotation processing을 체크해준다.

컴파일이 오류없이 잘 되는지 확인한다.


lombok 설치 및 설정이 잘 된 것을 확인했다.


1. POST Method 만들기



controller 패키지에 PostAPIController.java를 생성해준다.



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package com.example.test.controller;
 
import com.example.test.model.SearchVO;
import org.springframework.web.bind.annotation.*;
 
@RestController
@RequestMapping("/api")
public class PostAPIController {
 
    // http통신할때 post는 body에다 data를 집어넣어서 받아오겠다.
    // @RequestBody에 SearchVO에 있는 값을 매칭해서 보내줘라.
    
    @RequestMapping(method = RequestMethod.POST, path = "/postRequest")
    public SearchVO postRequest(@RequestBody SearchVO searchVo){
        return searchVo;
    }
 
    @PostMapping(value = "/postMapping")
    public SearchVO postMapping(@RequestBody SearchVO searchVo){
        return searchVo;
    }
 
}
cs


위와 같이 POST 메소드를 작성한다.


GET 메소드와는 다르게 POST 메소드는 @RequestBody 어노테이션을 사용한다.

프론트에서 http 통신 시 post 메소드는 data를 body안에 담아서 가져온다.

body에 담아올때 메소드에 진입하면 모델이 가지고 있는 data만 json형태로 받아온다.


2. 테스트 해보기


크롬 웹스토어로 가서 REST API 확장 프로그램을 설치한다.

https://chrome.google.com/webstore/category/extensions?hl=ko



설치가 완료되면 POST 메소드로 json형태의 BODY를 작성하여 Send한다.



Response 200 으로 정상적으로 POST 된 것을 확인했다.



모델(SearchVO)에 있는 data만 json형태로 가져온것을 확인 할 수 있다.

모델에 존재하지 않은 param값을 프론트에서 보내면 @RequestBody에서 받아오지 못한다.


POST 메소드 구현 및 테스트를 완료했다.


REST API 구현하기


HTTP Method GET


1. GET 메소드 구현하기



com.example.test 아래에 controller 패키지를 생성하고 GetAPIController.java 파일을 생성한다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package com.example.test.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
 
@RestController // 여기는 컨트롤러라고 알려주는 @RestController 어노테이션 사용
@RequestMapping("/api"// 여기로 들어올 path를 지정할 @RequestMapping 어노테이션 사용 localhost:8080/api
public class GetAPIController {
 
    @RequestMapping(method = RequestMethod.GET, path = "/getRequest")   // localhost:8080/api/getRequest
    public String getRequest(){
        return "this is getRequest";
    }
 
}
cs


GetAPIController.java 의 내용을 위와 같이 넣어준다.

그리고 웹브라우저를 통해 http://localhost:8080/api/getRequest로 접속하여 결과가 나오는지 테스트한다.




결과가 잘 나오는 것을 확인했다.


하지만 GET Method는 웹에서 파라미터가 뒤에 계속 붙으면서 보여진다.

이런식으로 http://localhost:8080/api/getRequest?id=shlee0882&email=shlee0882@gmail.com

요청해야하는데 파라미터를 받아서 처리해보자.


2. 파라미터 받아서 GET 메소드 처리하기


1
2
3
4
5
    // GetMapping은 RequestMapping과는 다르게 메소드유형 없이 주소만 지정해주면된다.
    @GetMapping("/getParameters")  // localhost:8080/api/getParameters?id=shlee0882&email=shlee0882@gmail.com
    public String getParameters(@RequestParam String id, @RequestParam String email){
        return "아이디는 "+id+" 이메일은 "+email;
    }
cs


@GetMapping이라는 어노테이션을 사용했다.

@GetMapping은 @RequestMapping과 다르게 메소드유형 없이 path만 지정해 주면 된다.

메소드로 받을 인자값 2개를 설정해 주었다.

웹브라우저를 통해 파람값을 붙여서 요청했다.

http://localhost:8080/api/getRequest?id=shlee0882&email=shlee0882@gmail.com

결과가 잘 나오는 것을 확인했다.



위의 메소드는 인자값이 Request 파라미터값이 되기 때문에

메소드의 인자값을 바꿔주고 싶을때는 RequestParam의 name을 지정해주고 

받아온 param값의 변수를 새롭게 지정하여 활용할 수 있다.


1
2
3
4
5
    @GetMapping("/getParameters")  
    public String getParameters(@RequestParam(name = "id"String userId
                              , @RequestParam(name = "email"String userEmail){
        return "아이디는 "+userId+" 이메일은 "+userEmail;
    }
cs


하지만 만약 프론트에서 넘어온 데이터 RequestParam값이 10개 이상이면 

매번 RequestParam을 선언해주며 인자를 만들수는 없다.


이럴경우 모델(Model) 객체를 활용하여 받아올수 있다.



model 패키지를 생성하여 SearchVO.java를 만든다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package com.example.test.model;
 
public class SearchVO {
    private String id;
    private String email;
    private int page;
 
    public String getId() {
        return id;
    }
 
    public void setId(String id) {
        this.id = id;
    }
 
    public String getEmail() {
        return email;
    }
 
    public void setEmail(String email) {
        this.email = email;
    }
 
    public int getPage() {
        return page;
    }
 
    public void setPage(int page) {
        this.page = page;
    }
}
cs


SearchVO.java의 Getter, Setter 메소드 모델을 작성해준다.


1
2
3
4
    @GetMapping("/getMultiParameters")
    public String getMultiParameters(SearchVO searchVo) {
        return "VO사용 아이디는 "+searchVo.getId()+" 이메일은 "+searchVo.getEmail();
    }
cs


http://localhost:8080/api/getMultiParameters?id=shlee0882&email=shlee0882@gmail.com 요청한다.



객체를 사용하여 인자 1개만으로 여러개의 파라미터를 받아 처리하여 결과가 잘 나온것 확인했다.


하지만 API는 대부분 요청에 대한 응답 결과값을 json형태로 받는다.

결과값을 json 형태 { id : shlee0882, email : shlee0882@gmail.com } 이런식으로 return 받으려면 다음과 같이 작성할 수 있다. 


1
2
3
4
    @GetMapping("/getMultiParametersRtnJson")
    public SearchVO getMultiParametersRtnJson(SearchVO searchVo) {
        return searchVo;
    }
cs


http://localhost:8080/api/getMultiParametersRtnJson?id=shlee0882&email=shlee0882@gmail.com 요청한다.



결과가 json으로 받아지는 것을 확인했다.


Spring Boot 프로젝트 만들기 - 1


1. IntelliJ에서 프로젝트 생성하기


- JDK 12 설치


인텔리J를 사용하려면 높은 JDK버전이 필요하므로 JDK 12를 설치한다.


https://www.oracle.com/technetwork/java/javase/downloads/jdk12-downloads-5295953.html



- Intellij 설치




https://www.jetbrains.com/idea/download/#section=windows


인텔리J IDEA Ultimate 버전을 다운받는다.

평가판으로 일정기간 무료로 사용할수 있다.




위와 같이 체크해준다.

64비트, Update Path variable, 

Add "Open Folder as Project", .java, Download install JBR x86 (젯브레인에서 제공하는 라이브러리도 설치) 한다.


설치가 완료되면 바탕화면에 바로가기 아이콘이 생성되고 intellij를 실행한다.



기본설정을 선택하고 OK버튼을 누른다.



본인이 원하는 테마를 선택하고 다음 설정으로 넘어간다.



커스터마이징 하는부분인데 기본설정으로 다음으로 넘어간다.



다음버튼을 누른다.



30일 평가판으로 설치한다.



설치가 완료되면 다음과 같이 나타난다.

Create New Project를 누른다.



Project SDK가 JDK 12로 잡혀져있는지 확인한다.

왼쪽 메뉴에 Spring Initializer를 선택하고 Next를 누른다.



이름은 test로 Gradle Project를 선택하고 기본셋팅으로 Next를 누른다.



새 프로젝트를 어떻게 구성할지 체크해서 시작할수 있다.

간단하게 Web > Spring Web Starter를 체크하여 Next 버튼을 누른다.



Finsh 버튼을 누른다.



그러면 다음과 같이 import Module from Gradle 이라는 창이 뜨고

Use auto-import, Use default Gradle wrapper를 선택해주고 OK버튼을 누른다.



sync가 finished 되면 프로젝트를 실행시켜본다. 

Shift+F10 단축키나 TestApplication.java 파일로 접근하여 실행버튼을 누르면 된다.



Tomcat started on port 8080이 나타나면 성공이다.

Spring Boot 개발환경 설정을 완료했다.



2. Spring Initializer 활용하여 생성하기


https://start.spring.io/ 로 접속한다.



Dependencies에서 필요한 것들을 추가한 다음

Generate the project로 생성한다.


zip파일로 다운 되는데 압축을 푼다.


intelliJ를 실행하여 File > Open 으로 압축푼 폴더를 지정한다.




Use-auto-import를 체크해주고 OK 버튼을 누른다.




생성이 완료 되고 프로젝트를 실행하면 잘 구동되는 것을 확인 했다.


쿼리에서 조건을 걸어 데이터가 return 0 row일때

쿼리에서 "값이 없다" 라는 결과를 리턴값으로 처리하는 방법을 사용해보려한다.


1. 테이블 생성하기


1
2
3
4
5
6
CREATE TABLE `CODE_TABLE` (
    `CODE_CD` VARCHAR(10NOT NULL COLLATE 'utf8_bin',
    `CODE_NM` VARCHAR(10NOT NULL COLLATE 'utf8_bin'
)
COLLATE='utf8mb4_unicode_ci'
ENGINE=InnoDB;
cs


코드테이블이라는 테스트 테이블을 생성한다.

코드테이블은 코드, 코드명 컬럼으로 구성된다.


2. 데이터 넣기


1
2
3
4
5
INSERT INTO CODE_TABLE VALUES('10','개발팀');
 
INSERT INTO CODE_TABLE VALUES('20','영업팀');
 
INSERT INTO CODE_TABLE VALUES('30','기획팀');
cs


기초데이터를 넣어준다.



3. 없는 데이터 조회하기


1
2
3
4
-- SELECT한 Row가 0이므로 IFNULL 처리 불가        
SELECT IFNULL(A.CODE_NM, '없는 코드') AS CODE_NM
  FROM CODE_TABLE A
 WHERE A.CODE_CD = '100';
cs


현재 CODE_CD컬럼의 데이터는 10,20,30 이 들어가 있고

where절에서 '100' 을 조회하면 데이터가 조회되지 않는다.


위와 같이 쿼리를 작성하면 데이터가 있다면 코드명이 나오고

데이터가 없다면 아무것도 나오지 않는다.


SELECT 한 결과가 0건이라도

IFNULL에 걸린 것 처럼 "없는 코드" 라고 결과가 나오게 하고 싶다.



4. DUAL 테이블과 외부조인 걸기


1
2
3
4
     SELECT IFNULL(A.CODE_NM, '없는 코드') AS CODE_NM
       FROM CODE_TABLE A 
RIGHT OUTER JOIN (SELECT '') AS m_dual
         ON A.CODE_CD = '100';
cs



DUAL을 외부조인으로 사용하여 CODE가 없다면 "없는코드", 있다면 코드명이 나온다.


5. 집계함수 사용하기


1
2
3
     SELECT IFNULL(MAX(A.CODE_NM), '없는 코드') AS CODE_NM
       FROM CODE_TABLE A 
      WHERE A.CODE_CD = '100';
cs



집계함수를 사용하여 CODE가 없다면 "없는코드", 있다면 코드명이 나온다.


프로시저나 함수를 작성할때 

결과값이 없더라도 MESSAGE로 출력하고 싶다면 유용하게 쓰일수 있을 것 같다.


MySQL function을 이용해 직원 출석등록 함수를 만들어보겠다.


1. 테이블 생성하기


1
2
3
4
5
6
7
CREATE TABLE `EMP_ATTEND` (
    `ATTEND_YMD` VARCHAR(8NOT NULL COLLATE 'utf8_bin',
    `EMP_NO` VARCHAR(9NOT NULL COLLATE 'utf8_bin'
)
COLLATE='utf8mb4_unicode_ci'
ENGINE=InnoDB;
 
cs


간단하게 직원출석 테이블을 생성한다.



2. 함수 생성하기


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
DELIMITER $$
 
DROP FUNCTION IF EXISTS FNC_ATTEND;
 
CREATE FUNCTION FNC_ATTEND(attendType VARCHAR(10), empNo VARCHAR(10)) RETURNS BOOL
 
BEGIN
     DECLARE exist_flag INT;
     DECLARE returnVal  BOOL;
         SET returnVal = FALSE;
 
     -- 출석 등록
     IF 'attend'= attendType THEN
         SELECT COUNT(1)
           INTO exist_flag
           FROM EMP_ATTEND
          WHERE EMP_NO = empNo 
            AND ATTEND_YMD = DATE_FORMAT(NOW(), '%Y%m%d')
         ;
         
         IF exist_flag = 0 THEN
             INSERT INTO EMP_ATTEND(
                      ATTEND_YMD
                    , EMP_NO
             )VALUES(
                   DATE_FORMAT(NOW(), '%Y%m%d')
                     , empNo 
             );
             SET returnVal = TRUE;
         END IF;
     END IF;
      RETURN returnVal;
END $$
 
DELIMITER ;
cs


function의 argument 2개를 지정해 생성하고 return 값은 boolean으로 받겠다.


만약 arg로 들어온 첫번째 값이  문자열 'attend' 일경우 

테이블에 해당 직원의 오늘날짜로 출석한 데이터가 있는지 확인하고

데이터가 없다면 오늘날짜와 직원번호로 값을 insert한다.


값이 insert 된 후 returnVal의 값은 TRUE가 된다.



3. 함수 호출하기


1
2
3
4
-- if return value 0 then false
-- else if return value 1 then true
 
SELECT FNC_ATTEND('attend''1000');
cs



함수를 생성하고 위와 같이 호출을 해본다.


2개 파람값을 넣고 호출하면 1이나 0이 나오게 된다.

0이 나오면 false, 1이 나오면 true이다.

 


4. 테이블 조회하기


1
SELECT * FROM EMP_ATTEND;
cs


직원출석 테이블을 조회하면 값이 잘 들어간 것을 확인할 수 있다.



EMP_NO 하나당 하루에 한번 출석등록을 할 수 있는 function을 만들어보았다.


Oracle에서는 CONNECT BY PRIOR, START WITH를 사용하여

자신의 ID와 연결된 부모 ID를 찾아가 계층적으로 쿼리결과를 뽑을 수 있다.

오라클에서만 제공하는 구문이다.


1
2
3
4
5
6
7
-- UPMENU_ID에 연결된 MENU_ID가 계층적으로 표현된다.
 
 SELECT MENU_ID
      , UPMENU_ID
   FROM SYS_MENUINFO A
CONNECT BY PRIOR MENU_ID = UPMENU_ID 
  START WITH UPMENU_ID = '00000'
cs


오라클 쿼리 결과



MySQL에서 위와 같은 계층형 쿼리로 구현하고자 한다면 Oracle에서 제공하는 구문을 사용할 수 없다.

아래는 테스트로 구현할 계층형 형태의 쿼리 결과이다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
동물
 ┗말
   ┗얼룩말
   ┗조랑말
      ┗망아지
 
 ┗닭
   ┗흰닭
      ┗흰병아리
           ┗흰달걀
 
   ┗검은닭
      ┗검은병아리
           ┗검은달걀
cs




MySQL에서 계층형 쿼리 구문이 따로 없어 

함수를 만들어 재귀함수를 사용해 구현해본다.


ANIMAL 테이블을 생성한다.


1
2
3
4
5
6
7
8
9
10
11
CREATE TABLE `ANIMAL` (
 
`id` int(10unsigned NOT NULL AUTO_INCREMENT,
 
`p_id` int(10unsigned default '0',
 
`nm` varchar(50),
 
primary key(`id`)
 
ENGINE=InnoDB DEFAULT CHARSET=utf8
cs


ANIMAL 테이블에 데이터를 넣어준다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
insert into ANIMAL(p_id, nm) values ( 0'동물');
 
insert into ANIMAL(p_id, nm) values ( 1'말');
 
insert into ANIMAL(p_id, nm) values ( 1'닭');
 
insert into ANIMAL(p_id, nm) values ( 2'얼룩말');
 
insert into ANIMAL(p_id, nm) values ( 2'조랑말');
 
insert into ANIMAL(p_id, nm) values ( 3'흰닭');
 
insert into ANIMAL(p_id, nm) values ( 3'검은닭');
 
insert into ANIMAL(p_id, nm) values ( 5'망아지');
 
insert into ANIMAL(p_id, nm) values ( 6'흰병아리');
 
insert into ANIMAL(p_id, nm) values ( 7'검은병아리');
 
insert into ANIMAL(p_id, nm) values ( 9'흰달걀');
 
insert into ANIMAL(p_id, nm) values ( 10'검은달걀');
cs


함수 fnc_hierarchi 를 만들어준다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
DROP FUNCTION IF EXISTS fnc_hierarchi;
 
DELIMITER $$
 
CREATE FUNCTION  fnc_hierarchi() RETURNS INT
 
NOT DETERMINISTIC
 
READS SQL DATA
 
BEGIN
 
    DECLARE v_id INT;
    DECLARE v_parent INT;    
    DECLARE CONTINUE HANDLER FOR NOT FOUND SET @id = NULL;
 
    SET v_parent = @id;
    SET v_id = -1;
 
    IF @id IS NULL THEN
        RETURN NULL;
    END IF;
 
    LOOP
    
    SELECT MIN(id)
      INTO @id 
      FROM ANIMAL
     WHERE p_id = v_parent
       AND id > v_id;
 
    IF (@id IS NOT NULL) OR (v_parent = @start_with) THEN
       SET @level = @level + 1;
    RETURN @id;
    END IF;
    
    SET @level := @level - 1;
 
    SELECT id, p_id
      INTO v_id , v_parent 
        FROM ANIMAL
       WHERE id = v_parent;
   
    END LOOP;
 
END
 
$$
 
DELIMITER ;
cs


조회해본다.


1
2
3
4
5
6
7
8
9
10
11
12
SELECT CASE WHEN LEVEL-1 > 0 then CONCAT(CONCAT(REPEAT('    ', level  - 1),'┗'), ani.nm)
                 ELSE ani.nm
           END AS nm
     , ani.id
     , ani.p_id
     , fnc.level
  FROM
     (SELECT fnc_hierarchi() AS id, @level AS level
        FROM (SELECT @start_with:=0, @id:=@start_with, @level:=0) vars
          JOIN ANIMAL
         WHERE @id IS NOT NULL) fnc
  JOIN ANIMAL ani ON fnc.id = ani.id
cs


의도한 대로 쿼리결과가 나온것을 확인했다.



+ Recent posts