네로개발일기

개발자 네로의 개발 일기, 자바를 좋아합니다 !

'전체 글'에 해당되는 글 194건


반응형

Optional을 사용하냐, 그럼 언제 사용해야할까, 에 대한 질문과 답을 적으려고 한다.

요점

- NPE을 방지하기 위해 사용하는 클래스.

- null이 들어올 수 있는 값을 한 번 감싸는 Wrapper 클래스.

NPE (Null Pointer Exception)

null 참조로 인해 널 포인터 예외(NPE)가 발생하는 것이 가장 문제이다. 컴파일 시점에서는 문제가 없지만 런타임때 NPE가 발생한다면 가장 골치아프다.

java.lang.NullPointerException
    at seo.dale.java.practice(OptionalTest.java:26)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:497)

NULL 처리가 취약한 코드

null 처리가 취약한 코드에서 NPE 발생 확률이 높다.

"어떤 주문을 한 회원이 어느 도시에 살고 있는지 알아내기"에 관한 메서드가 다음과 같다고 하자.

// 주문을 한 회원이 살고있는 도시를 반환한다.
public String getCityOfMemberFromOrder(Order order) {
    return order.getMember().getAddress().getCity();
}

위 메서드는 NPE 위험에 노출된 상태이다.

1) order 파라미터에 null 일 경우

2) order.getMember() 의 결과가 null 일 경우

3) order.getMember().getAddress() 의 결과가 null 일 경우

적절히 null 처리를 하지 않으면, NPE가 발생할 수 있다.

JAVA8 이전의 NPE 방어 패턴

NPE의 위험에 노출된 코드를 다음과 같은 코딩 스타일로 회피하였다.

public String getCityOfMemberFromOrder(Order order) {
    if (order != null) {
        Member member = order.getMember();
        if (member != null) {
            Address address = member.getAddress();
            if (address != null) {
                String city = address.getCity();
                if (city != null) {
                    return city;
                }
            }
        }
    }

    return "Seoul"; // default
}

객체 탐색의 모든 단계마다 null 체크를 하고 있다. 들여쓰기 때문에 코드를 한눈에 읽을 수 없어 핵심 비즈니스 파악이 어렵다.

public String getCityOfMemberFromOrder(Order order) {
    if (order == null) {
        return "Seoul";
    }
    Member member = order.getMember();
    if (member == null) {
        return "Seoul";
    }
    Address address = member.getAddress();
    if (address == null) {
        return "Seoul";
    }
    String city = address.getCity();
    if (city == null) {
        return "Seoul";
    }
    return city;
}

결과값을 여러 곳에서 리턴하기 때문에 유지보수의 어려움이 생길 수 있다.

2가지 방법 모두 기본적으로 객체의 필드나 메서드에 접근하기 전에 null 체크를 하면서 NPE 를 방지하고 있다.

결국 비즈니스 로직의 파악이 어려워지고 코드 가독성과 유지보수성을 희생해야하는 코드가 나오게 되었다.

Java8 이전 문제

1. 런타임에 NPE가 발생할 수 있다.

2. NPE 방어를 위해 들어간 null 체크 로직 때문에 코드 가독성과 유지 보수성이 떨어진다.

함수형 언어에서 해법 찾기

스칼라같은 소위 함수형 언어들은 전혀 다른 방법으로 이 문제를 해결하고 있다. 자바가 "존재하지 않는 값"을 표현하기 위해서 null을 사용하였다면, 함수형 언어들은 _"존재할지 안할지 모르는 값"_을 표현할 수 있는 별개의 타입이 있다. 이 타입이 존재할지 안할지 모르는 값을 제어할 수 있는 여러가지 API를 제공하기 때문에 해당 API를 통해서 간접적으로 값에 접근할 수 있다. Java8은 java.util.Optional 라는 새로운 클래스를 도입하였다.

Optional 이란?

존재할 수도 있지만 안 할 수도 있는 객체, 즉 null이 될 수도 있는 객체를 감싸고 있는 일종의 래퍼 클래스이다.

Optional을 쓰면

장점

1. 객체가 null이어도 NPE가 발생하지 않고 비즈니스 로직대로 흘러갈 수 있다.

2. 비즈니스 코드와 null 방어 코드가 뒤섞여 있지 않아 로직을 더 쉽게 파악할 수 있다.

3. 명시적으로 해당 변수가 null일 수도 있다는 가능성을 표현할 수 있어 불필요한 방어 로직을 줄일 수 있다.

단점

1. null 체크 및 Wrapping 시 오버헤드가 발생하여 성능이 저하될 수있다. (null이 절대 나오지 않는 경우 Optional을 사용하지 않는 것이 더 성능에 더 좋다.)

Optional 기본 사용법

Optional 변수 사용하기

제네릭을 제공하기 때문에 변수를 선언할 때 명기한 타입 파라미터에 따라서 감쌀 수 있는 객체의 타입이 결정된다.

변수명은 maybe나 opt같은 접두어를 부어 Optional 타입의 변수라는 것을 좀 더 명확히 나타내기도 한다.

Optional<Order> maybeOrder; // Order 타입의 객체를 감쌀 수 있는 Optional 타입의 변수

Optional 객체 생성하기

Optional 클래스는 간편하게 객체 생성을 할 수 있도록 3가지 정적 팩토리 메서드를 제공한다. (empty, of, ofNullable)

- Optional.empty()

null을 담고 있는(비어있는) Optional 객체를 가져온다. 이 비어있는 객체는 Optional 내부적으로 미리 생성해놓은 싱글턴 인스턴스이다.

Optional<Member> maybeMember = Optional.empty();

- Optional.of(value)

null이 아닌 객체를 담고있는 Optional 객체를 생성한다. null이 들어올 경우 NPE를 던지기 때문에 주의해야 한다.

Optional<Member> maybeMember = Optional.of(aMember);

- Optional.ofNullable(value)

null이 아닌지 확신할 수 없는 객체를 담고 있는 Optional 객체를 생성한다. null이 넘어올 경우 NPE를 던지지 않고 Optional.empty()와 동일하게 비어있는 Optional 객체를 가져온다. 해당 객체가 null인지 아닌지 자신없는 상황에서 이 메서드를 사용해야 한다.

Optional<Member> maybeMember = Optional.ofNullable(aMember);
Optional<Member> maybeNotMember = Optional.ofNullable(null);

Optional이 담고있는 객체 접근하기

Optional 클래스는 담고있는 객체를 꺼내기 위해 다양한 인스턴스 메서드를 제공한다. 아래 메서드는 모두 Optional이 담고있는 객체가 존재할 경우 동일하게 해당 값을 반환한다. 반면에 비어있는 경우 (즉, null일 경우) 메서드마다 다르게 동작한다.

- get()

비어있는 Optional 객체에 대해서, NoSuchElementException을 던진다.

- orElse(T other)

비어있는 Optional 객체에 대해서, 넘어온 인자를 반환한다.

- orElseGet(Supplier<? extends T> other)

비어있는 Optional 객체에 대해서, 넘어온 함수형 인자를 통해 생성된 객체를 반환한다.

- orElseThrow(Supplier<? extends X> exceptionSupplier)

비어있는 Optional 객체에 대해서, 넘어온 함수형 인자를 통해 생성된 예외를 던진다.

 

Optional을 잘못 사용할 경우

get() 메서드는 비어있는 Optional 객체를 대상으로 호출할 경우, 예외를 발생시키기 때문에 객체 존재여부를 boolean으로 반환하는 isPresent()라는 메서드를 통해 null 체크가 필요하다.

// Optional을 사용한 경우
String text = getText();
Optional<String> maybeText = Optional.ofNullable(text);
int length;
if (maybeText.isPresent()) {
    length = maybeText.get().length();
} else {
    length = 0;
}

// Optional을 사용하지 않은 경우
String text = getText();
int length;
if (text == null) {
    length = text.length();
} else {
    length = 0;
}

이렇게 사용할 경우 Optional을 사용해서 좋아진 부분이 없다.

Optional 적용 후에는 null 체크를 할 필요가 없다. (null 체크를 하지 않고 Optional에 위임하기 위해 Optional을 사용한다.)

 

Optional을 정확히 이해했다면 다음과 같이 한줄로 코드를 작성할 수 있어야 한다. 

int length = Optional.ofNullable(getText()).map(String::length).orElse(0);

 

 출처 

www.daleseo.com](https://www.daleseo.com/java8-optional-after/)

https://www.daleseo.com/java8-optional-after/

 

자바8 Optional 2부: null을 대하는 새로운 방법

Engineering Blog by Dale Seo

www.daleseo.com

 

https://maivve.tistory.com/332

 

[Java] JAVA8 Optional Class에 대한 정의 및 사용방법 - Tutorial(Sample)

https://github.com/201402407/JAVA_LOGIC_TUTORIAL GitHub - 201402407/JAVA_LOGIC_TUTORIAL: 자바의 기본기를 다지는 코딩 가이드 및 자바 튜토리얼 자바의 기본기를 다지는 코딩 가이드 및 자바 튜토리얼. Contribute to 2014

maivve.tistory.com

 

728x90
반응형
blog image

Written by ner.o

개발자 네로의 개발 일기, 자바를 좋아합니다 !

반응형

1. Spring Security Config 리팩토링

Spring Security 버전업이 되면서 기존 프로젝트의 문제점이 생기기 시작했다. 해당 문제점에 대해 알아보고 리팩토링을 하자.

 

  • WebSecurityConfigurerAdapter 상속 제거
  • Resource Filter Chain 설정

2. 개발 환경

  • Spring Boot 2.7.6
  • Spring Security 5.7.6
  • Java 11
  • Gradle 7.5.1

3. 기존 SecurityConfig

일반적으로 많이 사용하는 SecurityConfig이다.

@Configuration
@EnableWebSecurity // Spring Security 활성화
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    
    @Override
    public void configure(WebSecurity web) {
        // resource 에 대해 Spring Security FilterChain 제외
        web.ignoring().antMatchers("/resources/**");
    }
    
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .mvcMatchers("/", "/login", "/sign-up", "/check-email-token"
                        , "/email-login", "/check-email-login", "/login-link", "/login-by-email").permitAll()
                .mvcMatchers(HttpMethod.GET, "/profile/*").permitAll()
                .anyRequest().authenticated().and()
            .formLogin()
                .loginPage("/login").permitAll()
            .logout()
                .logoutSuccessUrl("/");
    }
}

ant pattern을 이용한 ignore 처리가 권장되지 않는다.

해당 설정으로 실행시 하단과 같은 WARN 로그가 발생한다.

You are asking Spring Security to ignore Ant [pattern='/resource/**']. This is not recommended -- please use permitAll via HttpSecurity#authorizeHttpRequests instead.

이 로그는 Spring Security 5.5.x에 추가되었으며 Spring Security Github Issue에서 확인할 수 있다. 간략히 요약해보자면,

web.ignoring()은 Spring Security 가 해당 엔드포인트에 보안헤더 또는 기타 보호 조치를 제공할 수 없다. 대신에 permitAll() 메서드를 사용하여 권한 검증 없이 요청을 보호할 수 있다. 이러한 이유때문에 해당 경고메시지를 알려주었다.

또한, static resource에 대해 SecurityFilterChain을 추가하는 방법에 대해서 알려주었는데 해당 방안은 밑에서 알아보겠습니다.

 

Spring Security 5.7.x 부터 WebSecurityConfigurerAdapter Deprecate 문제

Spring Security 5.7.x 부터 WebSecurityConfigurerAdapter가 Deprecated 되었다.

https://spring.io/blog/2022/02/21/spring-security-without-the-websecurityconfigureradapter

 

4. SecurityConfig 리팩토링

WebSecurityConfigurerAdapter를 상속하기않고 적용

WebSecurityConfigurerAdapter를 상속하지 않고 필터체인을 구성하는 방법으론 SecurityFilterChain을 Bean으로 선언하는 방법이 있다. 이때 HttpSecurity를 주입받아 사용하면 된다.

@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean   
    public WebSecurityCustomizer webSecurityCustomizer() {
        return web -> web.ignoring().antMatchers("/resources/**");   
    }

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .mvcMatchers("/", "/login", "/sign-up", "/check-email-token"
                        , "/email-login", "/check-email-login", "/login-link", "/login-by-email").permitAll()
                .mvcMatchers(HttpMethod.GET, "/profile/*").permitAll()
                .anyRequest().authenticated();

        http.formLogin()
                .loginPage("/login")
                .permitAll();

        http.logout()
                .logoutSuccessUrl("/");

        return http.build();
    }
}

WebSecurityConfigurerAdapter 상속을 제거하고 WebSecurityCustomizer 선언과 HttpSecurity의 build() 호출 후, 리턴하여 Bean으로 등록한다.

HttpSecurityConfiguration을 확인해보면 HttpSecurity에 기본적인 설정을 한 후, prototype으로 Bean을 등록한다. 따라서 매번 주입받을 때 마다 새로운 인스턴스를 주입받을 수 있다.

 

➤ 주의사항

WebSecurityConfigurerAdapter 상속과 SecurityFilterChain Bean 등록을 동시에 사용할 경우 아래와 같은 로그가 발생한다.

Caused by: java.lang.IllegalStateException: Found WebSecurityConfigurerAdapter as well as SecurityFilterChain. Please select just one.

 

Resource 용 SecurityFilterChain 적용

WebSecurityCustomizer 설정을 제거하고 @Order(0) 을 추가하여 먼저 FilterChain 을 타도록 지정한다. resources(css, js 등) 의 경우 securityContext 등에 대한 조회가 불필요 하므로 disable 합니다.

@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .mvcMatchers("/", "/login", "/sign-up", "/check-email-token"
                        , "/email-login", "/check-email-login", "/login-link", "/login-by-email").permitAll()
                .mvcMatchers(HttpMethod.GET, "/profile/*").permitAll()
                .anyRequest().authenticated();

        http.formLogin()
                .loginPage("/login")
                .permitAll();

        http.logout()
                .logoutSuccessUrl("/");

        return http.build();
    }

    @Bean
    @Order(0)
    public SecurityFilterChain resources(HttpSecurity http) throws Exception {
        return http.requestMatchers(matchers -> matchers.antMatchers( "/resources/**"))
                .authorizeHttpRequests(authorize -> authorize.anyRequest().permitAll())
                .requestCache(RequestCacheConfigurer::disable)
                .securityContext(AbstractHttpConfigurer::disable)
                .sessionManagement(AbstractHttpConfigurer::disable).build();
    }
}

 

 

 

 출처 

https://spring.io/blog/2022/02/21/spring-security-without-the-websecurityconfigureradapter

 

Spring Security without the WebSecurityConfigurerAdapter

<p>In Spring Security 5.7.0-M2 we <a href="https://github.com/spring-projects/spring-security/issues/10822">deprecated</a> the <code>WebSecurityConfigurerAdapter</code>, as we encourage users to move towards a component-based security configuration.</p> <p

spring.io

https://github.com/spring-projects/spring-security/issues/10938

 

WARN when ignoring antMatchers - please use permitAll · Issue #10938 · spring-projects/spring-security

When I use web.ignoring().antMatchers() I'd like to see a DEBUG message instead of a WARNING for each ignored pattern. I'm confused by the message saying it's not recommended and I shou...

github.com

https://velog.io/@csh0034/Spring-Security-Config-Refactoring

 

[Spring Security] Config Refactoring

WebSecurityConfigurerAdapter Deprecated, Lambda DSL 적용, Resource Filter Chain 설정

velog.io

 

728x90
반응형
blog image

Written by ner.o

개발자 네로의 개발 일기, 자바를 좋아합니다 !

반응형

fetch() 함수로 원격 API 호출하기

서버단에서 대신 API를 호출해주기 보다는 클라이언트 단에서 직접 API를 호출하는 경우가 많다. (소위 Ajax로 브라우저에서 직접 비동기로 HTTP 통신을 하기도 한다.)

 

원격 API를 간편하게 호출할 수 있도록 브라우저에서 제공하는 fetch() 함수에 대해 알아보자.

 

XML Http Request 방식

xmlhttprequest 객체를 이용한 정통적인 초창기 비동기 서버 요청방식이다. 코드가 복잡하고 가독성이 좋지 않다는 단점이 있다.

var httpRequest = new XMLHttpRequest();

httpRequest.onreadystatechange = function() {
  if (httpRequest.readyState == XMLHttpRequest.DONE && httpRequest.status == 200) {
    document.getElementById("text").innerHtml = httpRequest.responseText;
  }
}

httpRequest.open("GET", "ajac_intro_data.txt", true);
httpRequest.sent();

라이브러리

원격 API를 호출하면 request, axios, jQuery같은 라이브러리이다. 브라우저에서 fetch() 함수를 지원하기 전에 클라이언트 단에서 직접 HTTP 요청하고 응답받는게 복잡해서 라이브러리를 사용했다. 요즘은 라이브러리 없이도 브라우저에 내장된 fetch() 함수를 이용하여 원격 API를 호출할 수 있다. 라이브러리를 사용하면 자바스크립트 번들(bundle) 파일의 크기만 늘어난다.

 

fetch API 방식

이벤트 기반인 XMLHttpRequest와 달리 fetch API는 Promise 기반으로 구성되어 있어 비동기 처리 프로그래밍 방식에 잘 맞다. 그래서 then이나 catch와 같은 체이닝으로 작성할 수 있다는 장점이 있다.

 

fetch 사용법

// fetch('서버 주소') 는 웹 브라우저에 '이 서버 주소로 요청해줘' 라는 의미고,
// 뒤에 .then은 '요청이 끝나고나서 이 일을 진행해줘' 라는 의미다.
fetch('ajax_intro_data.txt')
  .then( response => response.text() )
  .tehn( text => { document.getElementById("#t").innerHtml = text; } );

fetch() 함수는 첫번째 인자로 url, 두번째 인자로 options 객체를 받고, Promise 타입의 객체를 반환한다. 반환된 객체는 API 호출이 성공했을 경우엔 응답 객체를 resolve 하고, 실패했을 경우에 예외객체를 reject 한다.

fetch(url, options) 
  .then((response) => console.log("response: ", response))
  .catch((error) => console.log("error: ", error));

1. 기본적으로 http 메서드 중 GET으로 동작한다.

2. 개발자 도구 > 네트워크 탭에 가면 fetch로 들어온 데이터를 볼 수 있다.

 

fetch의 response 속성

fetch를 통해 요청을 하고 서버로부터 값을 응답 받으면 .then을 통해 함수의 인자에 넘기게 되는데 이 값은 Response 객체이다.

- response.status : HTTP 상태 코드 

- response.ok : HTTP 상태코드가 200~299일 경우 true

- response.body : 내용

- response.text() : 응답을 읽고 텍스트를 반환한다.

- response.json() : 응답을 JSON 형태로 파싱한다.

- response.formData() : 응답을 FormData 객체 형태로 반환한다.

- response.blob() : 응답을 Blob(타입이 있는 바이너리 데이터) 형태로 반환한다.

- response.arrayBuffer() : 응답을 ArrayBuffer(바이너리 데이터를 로우 레벨 형식으로 표현한 것) 형태로 반환한다.

 

응답 자료 형태 반환 메서드는 한번만 사용 할 수 있다.
만일 response.text()를 사용해 응답을 얻었다면 본문의 콘텐츠는 모두 처리 된 상태이기 때문에 뒤에 또 response.json() 써줘도 동작하지 않게 된다.

 

Fetch - CRUD 요청하기

메서드 역할
GET GET을 통해 해당 리소스를 조회
POST POST를 통해 해당 URI를 요청하면 리소스를 생성
PUT PUT를 통해 해당 리소스를 수정
DELETE DELETE를 통해 리소스 삭제

GET 메서드

존재하는 자원을 요청

단순히 원격 API에 있는 데이터를 가지고 올 때 쓰임

fetch 함수는 디폴트로 GET방식으로 작동하고 옵션 인자가 필요가 없다.

응답(response) 객체는 json() 메서드를 제공하고, 이 메서드를 호출하면 응답객체로부터 JSON 형태의 데이터를 자바스크립트 객체로 변환하여 얻을 수 있다.

fetch("https://jsonplaceholder.typicode.com/posts/1") // posts의 id 1인 엘리먼트를 가져옴 
  .then((response) => response.json())
  .then((data) => console.log(data))
{
  "userId": 1,
  "id": 1,
  "title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
  "body": "quia et suscipit↵suscipit recusandae consequuntur …strum rerum est autem sunt rem eveniet architecto"
}

GET 메서드

새로운 자원 생성 요청

폼 등을 통해서 데이터를 만들어보낼 때, 보내는 데이터의 양이 많거나, 비밀번호 등 개인정보를 보낼 때 사용

method를 POST로 지정해주고, headers 옵션으로 JSON 포맷으로 적용해야 함. body 옵션은 요청 데이터를 JSON 포맷으로 넣어줌.

fetch("https://jsonplaceholder.typicode.com/posts", {
  method: "POST", 
  headers: { 
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    title: "Test",
    body: "I am testing!",
    userId: 1,
  }),
})
  .then((response) => response.json())
  .then((data) => console.log(data))

PUT 메서드 (전체 수정)

존재하는 자원 변경 요청

API에서 관리하는 데이터의 수정을 위해 PUT 메서드를 사용함

method 옵션만 PUT으로 설정하는 점이외에는 POST와비슷

아예 전체를 body의 데이터로 교체해버림

fetch("https://jsonplaceholder.typicode.com/posts", {
  method: "PUT",
  headers: {
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    title: "Test" // 아예 title 엘리먼트로 전체 데이터를 바꿈. 마치 innerHTML같이.
  }),
})
  .then((response) => response.json())
  .then((data) => console.log(data))

PATCH 메서드 (부분 수정)

존재하는 자원의 일부 변경 요청

fetch("https://jsonplaceholder.typicode.com/posts/1", { // posts의 id 1인 엘리먼트를 수정
  method: "PATCH",
  headers: {
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    title: "Test" // title만 바꿈. 나머지 요소는 건들지 않음.
  }),
})
  .then((response) => response.json())
  .then((data) => console.log(data))

DELETE 메서드

존재하는 자원 삭제 요청

보낼 데이터가 없기 때문에 headers, body 옵션이 필요가 없다.

fetch("https://jsonplaceholder.typicode.com/posts/1", {
  method: "DELETE",
})
  .then((response) => response.json())
  .then((data) => console.log(data))

Fetch - async / await 문법

fetch의 리턴값 response 는 Promise 객체이다. 따라서 await / async 문법으로 가독성높게 코딩할 수 있다.

fetch("https://jsonplaceholder.typicode.com/posts", option)
.then(res => res.text())
.then(text => console.log(text));

//await은 async함수내에서만 쓸수 있으니, 익명 async 바로 실행함수를 통해 활용합니다.
(async () => {
  let res = await fetch("https://jsonplaceholder.typicode.com/posts", option);
  let text = res.text();
  console.log(text);
})()

fetch 모듈화 - 사용성 개선하기

fetch() 함수는 사용법이 아주 간단하지만, 계속 사용하다보면 똑같은 코드가 반복된다는 것을 느낄 것이다.

예를 들어, 응답 데이터을 얻기 위해서 response.json()을 매번 호출하거나, 데이터를 보낼 때, HTTP 요청 헤더에 "Content-Type": "application/json"로 일일히 설정해줘야 되거나 써야 될게 많다.

한두번 fetch() 를 사용하는 정도는 문제가 안되지만, 여러번 사용할 일이 있으면 따로 헬퍼 함수를 만들어 모듈화를 해놓는게 나중에 정말 편하게 사용할수 있다.

 

아래 코드와 같이 POST 요청을 보낼 필요가 있다면 함수로 따로 미리 구성을 짜놓고, 인자로 값들을 전달해 바로 응답값을 받는 식으로 구성을 짜놓자.

async function post(host, path, body, headers = {}) {
  const url = `https://${host}/${path}`;
  const options = {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      ...headers,
    },
    body: JSON.stringify(body),
  };
  const res = await fetch(url, options);
  const data = await res.json();
  if (res.ok) {
    return data;
  } else {
    throw Error(data);
  }
}
 
post("jsonplaceholder.typicode.com", "posts", {
  title: "Test",
  body: "I am testing!",
  userId: 1,
})
  .then((data) => console.log(data))
  .catch((error) => console.log(error));

 

 

 

 

 출처 

https://www.daleseo.com/js-window-fetch

 

[자바스크립트] fetch() 함수로 원격 API 호출하기

Engineering Blog by Dale Seo

www.daleseo.com

https://inpa.tistory.com/entry/JS-%F0%9F%93%9A-AJAX-%EC%84%9C%EB%B2%84-%EC%9A%94%EC%B2%AD-%EB%B0%8F-%EC%9D%91%EB%8B%B5-fetch-api-%EB%B0%A9%EC%8B%9D

 

 

[JS] 📚 자바스크립트 Fetch API 으로 AJAX 요청하기

자바스크립트 AJAX 요청 방식 저번 시간에 Ajax 가 무엇인지 알아보았다. 정통적으로 XMLHttpRequest() 객체를 생성하여 요청하는 방법이 있지만 문법이 난해하고 가독성도 좋지 않다. 따라서 이번시간

inpa.tistory.com

 

728x90
반응형
blog image

Written by ner.o

개발자 네로의 개발 일기, 자바를 좋아합니다 !

반응형

CUBRID 테이블 컬럼 추가 및 변경 SQL

CUBRID 테이블 컬럼 추가 및 변경은 add/rename/drop column, change, modify가 사용되며 방법은 아래와 같다.

1. 컬럼 추가 add column
ALTER TABLE sample_tbl ADD COLUMN src_name char(10) // 제일 뒤에 삽입
ALTER TABLE sample_tbl ADD COLUMN src_name2 char(10) first // 제일 앞에 삽입
ALTER TABLE sample_tbl ADD COLUMN src_name3 char(10) after user_name // user_name 뒤에 삽입


2. 컬럼 이름, 크기, 속성 변경 change/modify
ALTER TABLE sample_tbl CHANGE src_name3 src_name4 varchar(200) // 컬럼 이름과 크기변경
ALTER TABLE sample_tbl MODIFY src_name4 varchar(100) not null default '' // 컬럼 크기와 속성 변경


3. 컬럼 이름 변경 rename column
ALTER TABLE sample_tbl RENAME COLUMN src_name4 as[to] nci_name // 컬럼 이름 변경


4. 컬럼 삭제 drop column
ALTER TABLE sample_tbl DROP COLUMN nci_name // 컬럼 삭제


5, 테이블 이름 변경 rename table
RENAME TABLE sample_tbl as [to] new_sample_tbl // 테이블 이름 변경

 

6. 참조사항

- change는 타입, 크기, 속성 변경, 컬럼 이름이 가능하고 modify는 타입, 크기, 속성 변경이 가능하지만 컬럼 이름 변경은 안된다.

 

7. 주의사항

- CHANGE 절이나 MODIFY 절로 새 칼럼에 적용할 타입, 크기 및 속성을 설정할 때 기존에 정의된 속성은 새 칼럼의 속성에 전달되지 않으며 데이터 타입을 변경할 때, 기존의 칼럼 값이 변경되면서 데이터가 변형될 수 있다. 예를 들어 문자열 칼럼의 길이를 줄이면 문자열이 잘릴 수 있으므로 주의해야 한다.

 

 

 출처 

https://www.cubrid.com/faq/3794756

 

CUBRID | FAQ & TIP - 테이블 컬럼 변경 및 추가

CUBRID 테이블 컬럼 추가 및 변경은 add/rename/drop column, change, modify가 사용되며 방법은 아래와 같다.1, add columnalter table sample_tbl add column src_name char(10) // 제일 뒤에 삽입alter table sample_tbl add column src_nam

www.cubrid.com

 

 
728x90
반응형
blog image

Written by ner.o

개발자 네로의 개발 일기, 자바를 좋아합니다 !

반응형

1. git reflog 로 이전 커밋 내역 출력

- git rebase 또는 git reset 등으로 커밋이 삭제될 수 있다.

- 하지만, git 이력은 보관되고 있다. 이런 모든 이력을 볼 수 있는 명령어가 git reflog 이다.

 

2. commit 복구하기

1. git reflog 명령어로 삭제된 commit id 확인 후

2. git reset --hard <캐시해시id>

 

3. branch 복구하기

1. git reflog 또는 git reflog | grep [브랜치명] 으로 log 확인

2. git checkout -b [삭제한 브랜치명] [커밋해시id]

728x90
반응형
blog image

Written by ner.o

개발자 네로의 개발 일기, 자바를 좋아합니다 !