1. 배열 전송하기

Client
Method : POST
Content-type : application/x-www-form-urlencoded; charset=UTF-8

    $.ajax({
        url: "/test",
        dataType: "json",
        contentType: "application/x-www-form-urlencoded; charset=UTF-8",
        type: "post",
        data: {ids : [1,2,3]},
        success: function (res) {
        },
        error: function (request, status, error) {
        }
    });

Server

    @PostMapping(value = "/test")
    @ResponseBody
    @ResponseStatus(HttpStatus.NO_CONTENT)
    public void test(
            @RequestParam(name = "ids[]", required = false) List<Integer> ids
    ) {
        logger.info("ids : {}", ids);
    }

2. 배열 전송하기(JSON)

Client
Method : POST
Content-type : application/json; charset=UTF-8

    $.ajax({
        url: "/test",
        dataType: "json",
        contentType: "application/json; charset=UTF-8",
        type: "post",
        data: JSON.stringify([1, 2, 3]),
        success: function (res) {
        },
        error: function (request, status, error) {
        }
    });

Server

    @PostMapping(value = "/test")
    @ResponseBody
    @ResponseStatus(HttpStatus.NO_CONTENT)
    public void test(
            @RequestBody(required = false) List<Integer> ids
    ) {
        logger.info("ids : {}", ids);
    }

3. 배열과 데이터 전송하기(JSON)

Client
Method : POST
Content-type : application/json; charset=UTF-8

    $.ajax({
        url: "/test",
        dataType: "json",
        contentType: "application/json; charset=UTF-8",
        type: "post",
        data: JSON.stringify({ids: [1, 2, 3], name: "kim"}),
        success: function (res) {
        },
        error: function (request, status, error) {
        }
    });

Server

Json 데이터를 바인딩할 객체

public class Test {

    private List<Integer> ids;
    private String name;
    
    //setter, getter 생략
    
}
    @PostMapping(value = "/test")
    @ResponseBody
    @ResponseStatus(HttpStatus.NO_CONTENT)
    public void test(
            @RequestBody(required = false) Test test
    ) {
        logger.info("test : {}", test);
    }

API 요청 시, 요청한 클라이언트의 정보를 확인할 수 있는 spring-mobile-starter 라이브러리 사용방법을 포스팅하겠습니다.

1. pom.xml

...
<dependencies>
	 <dependency>
            <groupId>org.springframework.mobile</groupId>
            <artifactId>spring-mobile-starter</artifactId>
            <version>2.0.0.M3</version>
            <type>pom</type>
        </dependency>
</dependencies>
...

<repositories>         
      <repository>
            <id>spring-milestones</id>
            <name>Spring Milestones</name>
            <url>https://repo.spring.io/milestone</url>
      </repository>
</repositories>
...    

spring-mobile-starter 라이브러리를 추가하고, 해당 라이브러리가 저장 되어있는 repository 정보를 등록합니다.

2. TestController

@Controller
@RequestMapping(value = "/")
public class TestController {

    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    @GetMapping(value = "/deviceCheck")
    public String deviceCheck(
            Device device
    ) {

        if (device.isMobile()) {
            logger.info("[MYTEST] mobile user!");
        } else if (device.isTablet()) {
            logger.info("[MYTEST] tablet user!");
        } else {
            logger.info("[MYTEST] desktop user!");
        }

        logger.info("[MYTEST]Device : {} ", device);
        logger.info("[MYTEST]Device Platform : {}", device.getDevicePlatform());
        
        return "index";
    }
}

3. 결과 화면

안녕하세요. 이번에는 많은 곳에서 사용되고 있는 git 설치 방법에 대해 포스팅해보겠습니다.

1. Git 설치 파일 다운로드

아래 경로에서 os에 맞는 git 설치 파일을 다운로드 해주세요.

https://git-scm.com/

오른쪽 아래에 Download 2.27.0 for Windows 이미지를 클릭하면 됩니다.

2. Git 설치

1) 다운받은 Git 설치파일을 실행시켜 주세요.

2) 설치경로 입력

3) Git Select Components

기본으로 설정으로 진행합니다.

Windows Explorer intergration
Git Bash Here : 폴더에서 우클릭 시, 해당 경로를 기준으로 Git Bash 실행 
Git GUI Gere : 폴더에서 우클릭 시, 해당 경로를 기준으로 Git GUI 실행 

4) 시작 메뉴에 Git 추가 여부

5) Git을 사용할 에디터 선택

취향에 맞는 에디터를 설정하시면 됩니다.

6) Git Path environment

추천하는 설정으로 선택해줍니다.

Git을 Command line과 다른 회사에서 제작한 소프트웨어에서 실행할 수 있는 권한을 주는 옵션입니다.

7) Select HTTPS transport backend

8) 줄 바꿈 옵션 선택

9) Git Bash terminal 설정

10) choose the default behavior of 'git pull'

11) extra options

12) 설치 완료

13) Git 버전 확인 및 사용자 등록

특정 폴더에서 우클릭 하여 git Bash를 실행해줍니다.

git bash에서 git version을 확인해 줍니다.
git --version

 

git 계정을 입력해줍니다.

git config --global user.name git-username
git config --global user.email git-email

git 유저 정보 확인

git config --list

오류 처리는 프로그램을 개발하는데 있어서 매우 큰 부분을 차지한다. 오류를 예측해서 비정상적인 상황이 발생하지 않게 하는 것은 정말 중요하다.

1. @ControllerAdvice 란?

  • @Controller나 @RestController에서 발생한 예외를 한 곳에서 관리하고 처리할 수 있게 도와주는 어노테이션이다. 

2. @ControllerAdvice 예제 코드

  • 동일한 유형의 Error Response 반환
  • 확장성이 용이한 Custom Exception의 사용

1) ErrorCode.class

예외에 대한 정보를 담고있는 enum class 이다.
public enum ErrorCode {

    INVALID_PARAMETER(400, null, "Invalid Request Data"),
    COUPON_EXPIRATION(410, "C001", "Coupon Was Expired"),
    COUPON_NOT_FOUND(404, "C002", "Coupon Not Found");

    private final String code;
    private final String message;
    private final int status;

    public String getCode() {
        return code;
    }

    public String getMessage() {
        return message;
    }

    public int getStatus() {
        return status;
    }

    ErrorCode(final int status, final String code, final String message) {
        this.status = status;
        this.message = message;
        this.code = code;
    }
}

2) ErrorResponse.class

예외에 대한 응답 정보를 저장하는 클래스이다. 
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import org.springframework.validation.Errors;
import org.springframework.validation.FieldError;

public class ErrorResponse {

    private LocalDateTime timestamp = LocalDateTime.now();
    
    private String message; //예외 메시지 저장

    private String code; // 예외를 세분화하기 위한 사용자 지정 코드,

    private int status; // HTTP 상태 값 저장 400, 404, 500 등..

    //@Valid의 Parameter 검증을 통과하지 못한 필드가 담긴다.
    @JsonInclude(JsonInclude.Include.NON_NULL)
    @JsonProperty("errors")
    private List<CustomFieldError> customFieldErrors; 
    

    public ErrorResponse() {
    }

    static public ErrorResponse create() {
        return new ErrorResponse();
    }

    public ErrorResponse code(String code) {
        this.code = code;
        return this;
    }

    public ErrorResponse status(int status) {
        this.status = status;
        return this;
    }

    public ErrorResponse message(String message) {
        this.message = message;
        return this;
    }

    public ErrorResponse errors(Errors errors) {
        setCustomFieldErrors(errors.getFieldErrors());
        return this;
    }

/*
getter 생략
*/

	//BindingResult.getFieldErrors() 메소드를 통해 전달받은 fieldErrors 
    public void setCustomFieldErrors(List<FieldError> fieldErrors) {

        customFieldErrors = new ArrayList<>();

        fieldErrors.forEach(error -> {
            customFieldErrors.add(new CustomFieldError(
                    error.getCodes()[0],
                    error.getRejectedValue(),
                    error.getDefaultMessage()
            ));
        });
    }

	//parameter 검증에 통과하지 못한 필드가 담긴 클래스이다.  
    public static class CustomFieldError {

        private String field;
        private Object value;
        private String reason;

        public CustomFieldError(String field, Object value, String reason) {
            this.field = field;
            this.value = value;
            this.reason = reason;
        }

        public String getField() {
            return field;
        }

        public Object getValue() {
            return value;
        }

        public String getReason() {
            return reason;
        }
    }
}

 

1) CustomException.class

쿠폰 만료 예외, 존재하지 않는 쿠폰 예외, 쿠폰 중복 참여 예외 등 여러가지 예외의 기본이 되는 클래스이다. 모든 예외 정보는 ErrorCode를 통해서 전달 받는다.
public class CustomException extends RuntimeException {

    private static final long serialVersionUID = 1L;

    private ErrorCode errorCode;

    public CustomException(ErrorCode errorCode) {
        super(errorCode.getMessage());
        this.errorCode = errorCode;
    }

    public ErrorCode getErrorCode() {
        return errorCode;
    }
}

2) CouponExpirationException.class

쿠폰이 만료 되었을 경우, 발생하는 예외 클래스이다. 
public class CouponExpirationException extends CustomException {

    private static final long serialVersionUID = -2116671122895194101L;

    public CouponExpirationException() {
        super(ErrorCode.COUPON_EXPIRATION);
    }
}

3) CouponNotFoundException.class

입력한 쿠폰을 찾을 수 없을 경우, 발생하는 예외 클래스이다.

public class CouponNotFoundException extends CustomException {

    private static final long serialVersionUID = -2116671122895194101L;

    public CouponNotFoundException() {
        super(ErrorCode.COUPON_NOT_FOUND);
    }
}

4) InvalidParameterException.class

public class InvalidParameterException extends CustomException {

    private static final long serialVersionUID = -2116671122895194101L;

    private final Errors errors;

    public InvalidParameterException(Errors errors) {
        super(ErrorCode.INVALID_PARAMETER);
        this.errors = errors;
    }

    public Errors getErrors() {
        return this.errors;
    }

}

5) ControllerExceptionHandler.class

@ControllerAdvice
public class ControllerExceptionHandler {

    private final Logger logger = LoggerFactory.getLogger(ControllerExceptionHandler.class);

    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    protected ResponseEntity<ErrorResponse> handleHttpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException e) {
        logger.error("handleHttpRequestMethodNotSupportedException", e);

        final ErrorResponse response
                = ErrorResponse
                        .create()
                        .status(HttpStatus.METHOD_NOT_ALLOWED.value())
                        .message(e.getMessage());

        return new ResponseEntity<>(response, HttpStatus.METHOD_NOT_ALLOWED);
    }

    //@Valid 검증 실패 시 Catch
    @ExceptionHandler(InvalidParameterException.class)
    protected ResponseEntity<ErrorResponse> handleInvalidParameterException(InvalidParameterException e) {
        logger.error("handleInvalidParameterException", e);

        ErrorCode errorCode = e.getErrorCode();

        ErrorResponse response
                = ErrorResponse
                        .create()
                        .status(errorCode.getStatus())
                        .message(e.toString())
                        .errors(e.getErrors());

        return new ResponseEntity<>(response, HttpStatus.resolve(errorCode.getStatus()));
    }

    //CustomException을 상속받은 클래스가 예외를 발생 시킬 시, Catch하여 ErrorResponse를 반환한다.
    @ExceptionHandler(CustomException.class)
    protected ResponseEntity<ErrorResponse> handleCustomException(CustomException e) {
        logger.error("handleAllException", e);

        ErrorCode errorCode = e.getErrorCode();

        ErrorResponse response
                = ErrorResponse
                        .create()
                        .status(errorCode.getStatus())
                        .code(errorCode.getCode())
                        .message(e.toString());

        return new ResponseEntity<>(response, HttpStatus.resolve(errorCode.getStatus()));
    }
    
    //모든 예외를 핸들링하여 ErrorResponse 형식으로 반환한다.
    @ExceptionHandler(Exception.class)
    protected ResponseEntity<ErrorResponse> handleException(Exception e) {
        logger.error("handleException", e);

        ErrorResponse response
                = ErrorResponse
                        .create()
                        .status(HttpStatus.INTERNAL_SERVER_ERROR.value())
                        .message(e.toString());

        return new ResponseEntity<>(response, HttpStatus.INTERNAL_SERVER_ERROR);
    }
}
  • @ControllerAdvice : Controller에서 발생한 예외를 Catch하여 처리하는 클래스이다. 
    basePackageClasses, basePackages 속성을 이용하여 특정 클래스에서 발생하는 예외만 받아서 처리할 수 있다.
  • @ExceptionHandler : 메소드 위에 선언되며, 등록한 예외를 Catch하여 처리할 수 있다.
  • ResponseEntity : Header와 Body에 응답 객체를 저장하여 반환할 수 있는 클래스이다.

6) Member.class

public class Member {

    @NotBlank(message = "name은 필수 값입니다.")
    private String name;

    @NotBlank(message = "email은 필수 값입니다.")
    private String email;

    @Min(value = 18, message = "18살 이상만 입장 가능합니다.")
    private int age;

    @NotNull(message = "금액은 필수 값 입니다.")
    @Min(value = 10000, message = "10000원 이상 소지해야합니다.")
    private Integer money;
    
    //getter,setter 생략
    
    
}

 

7) HomeController.class

@Controller
@RequestMapping(value = "/")
public class HomeController {

    @GetMapping("/member")
    public String memberException(@Valid Member dto, BindingResult result) {
        if (result.hasErrors()) {
            throw new InvalidParameterException(result);
        }

        return "page/home";
    }

    @GetMapping("/exception")
    public String exceptionTest(String code) {
        switch (code) {
            case "1":
                throw new CouponExpirationException();
            case "2":
                throw new CouponNotFoundException();
            case "3":
                int a = 3 / 0;
                break;
        }
        return "page/home";
    }
}

 

3. 테스트

1) GET http://localhost:8080/SpringBootException/member

에러 응답

{
    "timestamp": "2020-06-23T17:42:35.51",
    "message": "com.example.bamdule.exception.InvalidParameterException: Invalid Request Data",
    "status": 400,
    "fieldErrors": [
        {
            "field": "NotBlank.member.name",
            "value": null,
            "reason": "name은 필수 값입니다."
        },
        {
            "field": "Min.member.age",
            "value": 0,
            "reason": "18살 이상만 입장 가능합니다."
        },
        {
            "field": "NotBlank.member.email",
            "value": null,
            "reason": "email은 필수 값입니다."
        },
        {
            "field": "NotNull.member.money",
            "value": null,
            "reason": "금액은 필수 값 입니다."
        }
    ]
}
2) GET http://localhost:8080/SpringBootException/exception?code=2

에러 응답

{
    "timestamp": "2020-06-23T17:46:25.628",
    "message": "com.example.bamdule.exception.CouponNotFoundException: Coupon Not Found",
    "status": 404,
    "code": "C002"
}

 

참조 : https://cheese10yun.github.io/spring-guide-exception/

1. 디렉티브(Directive : 지시어)란?

HTML Element 안에 v- 접두어를 가진 attribute를 의미합니다. 표현식에 직접 값을 넣거나 미리 정의해둔 뷰 인스턴스와 데이터 바인딩하여 반응적으로 표현식을 변경할 수 있습니다.

디렉티브는 기본 디렉티브와 사용자 지정 디렉티브가 있다. 이번에는 기본 디렉티브에 대해 정리하겠습니다.

2. 디렉티브 종류

예시 뷰 인스턴스

new Vue({ 
  el: "#app", 
  data: {
    list: ['One','Two','Three','four','five','six'],
    toggle: true,
    message: 'hello!',
    imgUrl: 'https://img.test.png'
  },
  methods : {
  	switch : function(){
    	this.toggle = !this.toggle; 
    }
  }
})

1) v-bind

뷰 인스턴스와 데이터나 이벤트를 바인딩하거나 하위 컴포넌트에 데이터를 전달 할 때 사용합니다.
v-를 생략하고 :로 축약해서 사용할 수 있습니다.

...
<div id="app">
  <img v-bind:src="imgUrl"/>
    
  아래 방법으로도 가능합니다.
  <img :src="imgUrl"/>
</div>
...

2) v-model

뷰 인스턴스와 양방향으로 바인딩해서 뷰 인스턴스의 data 또는 Input 태그를 통해 바인딩한 값을 수정할 수 있습니다.

...
<div id="app">
  <input v-model="message" />
</div>
...

3) v-once

뷰 인스턴스와 한번만 렌더링을 수행합니다. 이 후 바인딩된 데이터가 변경되어도 처음에 입력한 값만 보여줍니다.

...
<div id="app">
  <input v-once="message" />
</div>
...

4) v-for

반복적으로 표현해야할 엘리먼트를 출력할 때 사용됩니다. 그리고 리스트 형식으로 바인딩된 데이터의 요소를 꺼내 사용할 수 있습니다.

...
<div id="app">
  <p v-for="(item, index) in list">
    [{{ index }}] {{item}} 
  </p>
</div>
...

5) v-if, v-else-if, v-else

v-if 디렉티브 값에 true, false 값을 넣어 반응적으로 엘리먼트를 표시하거나 숨길 수 있습니다.

...
<div id="app">
  <template v-if="toggle">
    <p>안녕하세요</p>
  </template>
</div>
...

6) v-text

v-text 디렉티브는 innerText에 값을 넣은 것과 동일한 기능이며, 값을 넣으면 태그에 해당 문자를 그대로 보여줍니다. 

...
<div id="app">
  <input v-bind:text="message" />
</div>
...

7) v-on

v-on 디렉티브는 엘리먼트에 이벤트를 바인딩할 때, 사용합니다.  기존에 사용했던 click, scroll 등.. 여러 이벤트를 사용할 수 있습니다.  v-on을 생략하고, @으로 축약해서 사용할 수 있습니다.

...
<div id="app">
  <template v-if="toggle">
    <p>안녕하세요</p>
  </template>
  <button @:click="switch"/>
</div>
...

'IT > Vue.js' 카테고리의 다른 글

[Vue.js] Vue.js 란?  (0) 2020.05.09

1. MVVM 패턴이란

MVVM 패턴은 페이지를 Model, View, ViewModel 단위로 분리해서 단위 별로 의존성을 줄이고, 화면 처리에 초점을 맞춘 개발 패턴입니다.

출처 : https://docs.microsoft.com/en-us/xamarin/xamarin-forms/enterprise-application-patterns/mvvm

2. View

화면을 그리는 레이아웃을 의미합니다. (HTML, XML 등..) View는 리스트나 상세 페이지 등 동적으로 데이터가 변경되는 부분에 View Model과 Data Binding하는 코드를 삽입합니다. 그리고 View Model를 감시하다가 상태 변화가 전달되면 화면을 갱신합니다.

View는 화면을 표현하는데 집중하고, 별도의 서비스 로직은 View Model를 통해 수행합니다.

3. View Model

View에 연결할 데이터(Model)와 명령(Command)으로 구성되어 있으며, Model로 부터 변경알림을 전달 받으면 View에게 변경 알림을 전달합니다. 명령은 View의 이벤트를 정의하며, View에 이벤트가 실행되면 해당 명령을 실행합니다.

4. Model

화면 표현하는데 필요한 데이터를 관리하며 사용자가 입력한 데이터를 저장하거나, 서버로 부터 받은 데이터를 저장합니다. 데이터 변경 시 View Model에게 변경 알림을 전송합니다. 

1. Vue.js 란

  • Vue.js는 MVVM(Model-View-ViewModel)을 기반으로 설계된 프론트엔드 프레임워크입니다.

2. Vue.js의 장점

  • UI를 Component 단위로 개발하여, 기능의 재사용이 쉽습니다. 
  • 가상 돔(Virtual DOM)을 사용하며, 다른 프론트엔드 프레임워크와 비교했을 때, 상대적으로 가볍고 빠릅니다.
  • 문법이 직관적이고 단순해서 초기 학습 비용이 낮습니다.
  • 다양한 모듈(vuex, vue-router, vue-validator, vue-resource...)를 제공합니다.

3. MVVM 패턴이란?

출처 : https://docs.microsoft.com/en-us/xamarin/xamarin-forms/enterprise-application-patterns/mvvm

MVVM 패턴은 페이지를 Model, View, ViewModel 단위로 분리해서 단위 별로 의존성을 줄이고, 화면 처리에 초점을 맞춘 개발 패턴입니다.

자세한 내용은 아래 링크를 이용해주세요.

2020/05/09 - [IT/디자인 패턴] - MVVM 패턴이란?

4. 가상 돔 (Virtual DOM)

돔은 트리 구조로 되어있고, 돔을 탐색하여 웹 페이지의 내용을 추가, 수정, 삭제할 수 있습니다. 돔을 이용해서 웹 페이지를 갱신하는 것은 쉽지만, 화면을 그리는 코드가 복잡해질수록 돔 트리의 Depth가 깊어지기 때문에 탐색 비용이 높아집니다.  

하지만 가상 돔(Virtual DOM)을 이용하면 불필요한 작업을 줄여서 효율적으로 화면을 렌더링할 수 있습니다.
그리고 가상 돔은 돔 관리를 자동화하고 추상화하여 개발자가 돔을 조작하지 않아도 되게 합니다.

결과적으로 가상 돔을 이용하면 성능뿐만 아니라 유지보수성도 향상 시킬 수 있습니다.

'IT > Vue.js' 카테고리의 다른 글

[Vue.js] 기본 디렉티브(Directive)  (0) 2020.05.12

1. GROUP_CONCAT 명령어

SELECT의 결과로 나온 여러 행의 특정 컬럼을 한줄로 출력할 때 사용한다.
(NULL 값은 제외된다.)

2. 사용 방법

GROUP_CONCAT([DISTINCT] [expr, column] [ORDER BY column DESC] [SEPARATOR '구분자'])

3. 사용 예제

 1) member 테이블

CREATE TABLE `member` (
	`id` INT(11) NOT NULL AUTO_INCREMENT,
	`NAME` VARCHAR(30) NULL DEFAULT NULL
	`age` INT(11) NULL DEFAULT NULL,
	PRIMARY KEY (`id`)
)

 2) member 데이터

INSERT INTO `member` (`NAME`, `age`) VALUES ('kim', 30);
INSERT INTO `member` (`NAME`, `age`) VALUES ('hong', 30);
INSERT INTO `member` (`NAME`, `age`) VALUES ('go', 31);
INSERT INTO `member` (`NAME`, `age`) VALUES ('lee', 33);
INSERT INTO `member` (`NAME`, `age`) VALUES ('son', 35);
INSERT INTO `member` (`NAME`, `age`) VALUES ('chang', 32);
INSERT INTO `member` (`NAME`, `age`) VALUES ('chei', 33);
INSERT INTO `member` (`NAME`, `age`) VALUES ('ho', 31);
INSERT INTO `member` (`NAME`, `age`) VALUES ('gu', 36);
INSERT INTO `member` (`NAME`, `age`) VALUES ('sang', 38);
INSERT INTO `member` (`NAME`, `age`) VALUES ('jang', 39);

 3) 쿼리

SELECT GROUP_CONCAT(name ORDER BY age DESC SEPARATOR ',') AS 'members'
FROM member

 4) 결과

jang,sang,gu,son,chei,lee,chang,ho,go,hong,kim

+ Recent posts