0111 - 0117

0111

새로운 프로젝트

오늘부터 인공지능 포털 사이트 개발 프로젝트에 참여하여 풀스택 영역을 다루기로 했다.
우선 백엔드 API개발부터 시작하였다.


  • 기존에 개발하던 API를 인수인계 받고 설명을 들었다.
  • 스토리 보드를 참조하여 내가 할 일을 정하고, 어떻게 구현할지 고민했다.
  • 개발 환경을 구축하고, 서버를 올리는 데 성공하였다.
  • 스프링 부트에 대한 정보를 찾아보며 기본적인 흐름에 대해 이해하였다.

0112

  • 회원과 관련된 API 개발을 맡아서 하게 되었다.
  • 회원가입과 회원탈퇴하는 API를 생성했다.
  • 회원가입 시 비밀번호를 SHA-256을 이용하여 암호화 해서 DB에 저장하였다.
public class SHA256Util {
    /**
     * SHA-256 암호화 함
     * @param source 원본
     * @param salt(String) SALT 값
     * @return
     */
    public static String getEncrypt(String source, String salt) {
        return getEncrypt(source, salt.getBytes());
    }

    /**
     * SHA-256 암호화 함
     * @param source 원본
     * @param salt(byte[]) SALT 값
     * @return
     */
    public static String getEncrypt(String source, byte[] salt) {

        String result = "";

        byte[] a = source.getBytes();
        byte[] bytes = new byte[a.length + salt.length];

        System.arraycopy(a, 0, bytes, 0, a.length);
        System.arraycopy(salt, 0, bytes, a.length, salt.length);

        try {
            // 암호화 방식 지정 메소드
            MessageDigest md = MessageDigest.getInstance("SHA-256");
            md.update(bytes);

            byte[] byteData = md.digest();

            StringBuffer sb = new StringBuffer();
            for (int i = 0; i < byteData.length; i++) {
                sb.append(Integer.toString((byteData[i] & 0xFF) + 256, 16).substring(1));
            }

            result = sb.toString();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }

        return result;
    }
}

//사용
        String password = user.getPassword();
        password = SHA256Util.getEncrypt(password, salt);

        user.setPassword(password);

처음으로 실제 프로젝트에서 사용하는 API를 개발하다보니 생각보다 고민해야 할 부분도 많았고, 코딩을 하기 전에 설계 측면에서 더 깊게 생각해야 한다는 것을 느꼈다.

0113

  • 비즈니스 형식으로 이메일을 작성해보고 이를 이용해 정보를 공유했다.
  • 로그인 시 쿠키를 생성해서 필요한 정보들을 쿠키에 저장하였다.
  • 그리고 쿠키를 이용해서 백엔드 로직을 구현하였다.
 Cookie[] loginCookies = new Cookie[4];  // 쿠키 설정
loginCookies[0] = new Cookie("email", login.getEmail());
loginCookies[1] = new Cookie("userNo", login.getUserNo() + "");
loginCookies[2] = new Cookie("userGbCode", login.getUserGbCode());
loginCookies[3] = new Cookie("userName", login.getUserName());

for (Cookie c : loginCookies) {
    c.setPath("/");
    c.setMaxAge(-1);
    response.addCookie(c);
}

//쿠키정보를 체크해서 회원인지를 확인한다. user.email과 쿠키에 있는 이메일이 같은지 확인하다.
Cookie[] cookies = request.getCookies();
if(cookies != null) {
    for(Cookie c : cookies){
        if ("email".equals(c.getName())) {
            cookieEmail = c.getValue();
        }
    }
}
int emailCnt = userService.dupEmail(user); //DB에 이메일이 존재하면 1, 없으면 0 반환
if (emailCnt > 0) {
    if(user.getEmail() == cookieEmail) {
        //회원탈퇴처리
        userService.withdraw(user);
        //회원탈퇴가 제대로 처리 되면 쿠키를 삭제한다.
        if (cookies != null) { // 쿠키가 한개라도 있으면 실행
            for (Cookie c : cookies) {
                c.setMaxAge(0); // 유효시간을 0으로 설정
                c.setPath("/");
                response.addCookie(c); // 응답 헤더에 추가
            }
        }
    }
}

쿠키는 이전에도 학습했던 적이 있는데, 직접 프로젝트에 사용해보니 조금 더 이해가 잘 되었다. 쿠키를 이용해서 API에 접근하는 권한을 확인하였다.

0114

  • 오늘은 스스로 여러가지 상황을 생각해서 오류 코드를 작성하였다.
  • API를 작성하는데 기본이 되는 연동규격 정의서를 작성하고, 그에 맞게 API를 개발하였다.
  • 직접 돌아가는 코드를 확인하고, 테스트를 시도하여 회원의 기본적인 CRUD를 할 수 있도록 설계 및 개발하였다.
  • 로그인 시 쿠키를 생성할 때, ‘UTF-8’형식으로 인코딩하여 디코딩 시 예상한 결과가 출력되도록 코딩하였다.

0115

java.math.BigInteger클래스

  • 정수형으로 표현할 수 있는 값의 한계가 있으므로, 더 큰 값을 다뤄야할 때 사용하면 좋은것이 BigInteger이다.
  • 내부적으로 int배열을 사용해서 값을 다뤄서 long타입보다 훨씬 큰 값을 다룰 수 있다. 대신 성능은 long타입보다 떨어진다.

BIgInteger생성

생성하는 방법은 여러 가지가 있는데, 문자열로 숫자를 표현하는 것이 일반적이다. 정수형 리터럴로는 표혈할 수 있는 값의 한계가 있기 때문이다.

BigInteger val = new BigInteger("12345678901234567890");	//문자열로 생성
BigInteger val = new BigInteger("FFFF", 16);	                //n진수 문자열로 생성
BigInteger val = BigInteger.valueOf(1234567890L);	        //숫자로 생성

다른 타입으로의 변환

String toString()		// 문자열로 변환
String toString(int radix)	// 지정된 진법의 문자열로 변환
byte[] toByteArray()	        // byte배열로 변환

BigInteger의 연산

BigInteger에는 정수형에 사용할 수 있는 모든 연산자와 수학적인 계산을 쉽게 해주는 메서드들이 정의되어 있다.

BigInteger add(BigInteger val)			// 덧셈(this + val)
BigInteger subtract(BigInteger val)		// 뺄셈(this - val)
BigInteger multiply(BigInteger val)		// 곱셈(this * val)
BigInteger divide(BigInteger val)		// 나눗셈(this / val)
BigInteger remainder(BigInteger val)	        // 나머지(this % val)
  • BigInteger는 불변이므로, 반환타입이 BigInteger이란 얘기는 새로운 인스턴스가 반환된다는 뜻

비트 연산 메서드

  • 워낙 큰 숫자를 다루기 위한 클래스이므로, 성능을 향상시키기 위해 비트단위로 연산을 수행하는 메서드들을 많이 가지고 있다.
  • 예를 들어 짝수는 제일 오른쪽 비트가 0일 것이므로, testBit(0)으로 마지막 비트를 확인하는 것이 더 효율적이다.

0116

java.math.BigDecimal클래스

BigDecimal은 실수형과 달리 정수를 이용해서 실수를 표현한다. 실수의 오차는 10진 실수를 2진 실수로 정확히 변환할 수 없는 경우가 있기 때문에 발생하는 것이므로, 실수를 정수와 10의 제곱의 곱으로 표현한다.

BigDecimal의 생성

생성하는 방법은 여러 가지가 있는데, 문자열로 숫자를 표현하는 것이 일반적이다. 기본형 리터럴로는 표혈할 수 있는 값의 한계가 있기 때문이다.

BigDecimal val = new BigDecimal("123.4567890");	//문자열로 생성
BigDecimal val = new BigDecimal("123.456");	// double타입의 리터럴로 생성
BigDecimal val = new BigDecimal("123456");	// int, long타입의 리터럴로 생성가능
BigDecimal val = BigDecimal.valueOf(123.456);	//생성자 대신 valueOf(double)사용
BigDecimal val = BigDecimal.valueOf(123456);	//생성자 대신 valueOf(int)사용
  • 주의할 점은, double타입의 값을 매개변수로 갖는 생성자를 사용하면 오차가 발생할 수 있다는 것

다른 타입으로의 변환

String toPlainString()	//어떤 경우에도 다른 기호없이 숫자로만 표현
String toString()	//필요하면 지수형태로 표현할 수도 있음
  • 대부분 두 메서드 반환결과가 같지만, BigDecimal을 생성할 때 ‘1.0e-22’와 같은 지수형태의 리터럴을 사용했을 때 다른 결과를 얻는 경우가 있다.

0117

HTTP 메서드 활용

클라이언트에서 서버로 데이터 전송

데이터 전달 방식 2가지

  1. 쿼리 파라미터를 통한 데이터 전송
    • 주로 정렬 필터(검색어)
  2. 메시지 바디를 통한 데이터 전송
    • 회원 가입, 상품 주문, 리소스 등록, 리소스 변경

4가지 상황

  1. 정적 데이터 조회
  2. 동적 데이터 조회
  3. HTML Form을 통한 데이터 전송
  4. HTTP API를 통한 데이터 전송

정적 데이터 조회

  • 이미지, 정적 텍스트 문서
  • 조회는 GET 사용
  • 정적 데이터는 일반적으로 쿼리 파라미터 없이 리소스 경로로 단순하게 조회 가능

동적 데이터 조회

  • 주로 검색, 게시판 목록에서 정렬 필터(검색어)
  • 조회 조건을 줄여주는 필터, 조회 결과를 정렬하는 정렬 조건에 주로 사용
  • 조회는 GET 사용
  • GET은 쿼리 파라미터 사용해서 데이터를 전달

HTML Form 데이터 전송

  • 회원 가입, 상품 주문, 데이터 변경시에는 POST 전송
  • Content-Type: application/x-www-form-urlencoded 사용
    • form의 내용을 메시지 바디를 통해서 전송(key=value, 쿼리 파라미터 형식)
    • 전송 데이터를 url encoding처리
  • HTML Form은 GET 전송도 가능하지만 조회에만 사용하고 리소스 변경이 발생하는 곳에 사용하면 안된다.
  • Content-Type: multipart/form-data
    • 파일 업로드 같은 바이너리 데이터 전송시 사용
    • 다른 종류의 여러 파일과 폼의 내용 함께 전송 가능
  • GET, POST만 지원

HTTP API 데이터 전송

  • 서버 to 서버
    • 백엔스 시스템 통신
  • 앱 클라이언트
    • 아이폰, 안드로이드
  • 웹 클라이언트
    • HTML에서 Form 전송 대신 자바 스크립트를 통한 통신에 사용(AJAX)
    • ex) React, VueJs같은 웹 클라이언트와 API 통신
  • POST, PUT, PATCH: 메시지 바디를 통해 데이터 전송
  • GET: 조회, 쿼리 파라미터로 데이터 전달
  • Content-Type: application/json을 주로 사용

HTTP API 설계 예시

회원 관리 시스템 API 설계 - POST 기반 등록

  • 회원 목록 /members -> GET
  • 회원 등록 /members -> POST
  • 회원 조회 /members/{id} -> GET
  • 회원 수정 /members/{id} -> PATCH, PUT, POST
  • 회원 삭제 /members/{id} -> DELETE

POST - 신규 자원 등록 특징

  • 클라이언트는 등록될 리소스의 URI를 모른다.
  • 서버가 새로 등록된 리소스 URI를 생성해준다.
    • HTTP/1.1 201 Created Location: /members/100
  • 컬렉션(Collection)
    • 서버가 관리하는 리소스 디렉토리
    • 서버가 리소스의 URI를 생성하고 관리
    • 여기서 컬렉션은 /members

파일 관리 시스템 API 설계 - PUT 기반 등록

  • 파일 목록 /files -> GET
  • 파일 조회 /files/{filename} -> GET
  • 파일 등록 /files/{filename} -> PUT
  • 파일 삭제 /files/{filename} -> DELETE
  • 파일 대량 등록 /files -> POST

PUT - 신규 자원 등록 특징

  • 클라이언트가 리소스 URI를 알고 있어야 한다.
  • 클라이언트가 리소스의 URI를 지정한다.
  • 스토어(Store)
    • 클라이언트가 관리하는 리소스 저장소
    • 클라이언트가 리소스의 URI를 알고 관리
    • 여기서 스토어는 /files

HTML FORM 사용

  • 회원 목록 /members -> GET
  • 회원 등록 폼 /members/new -> GET
  • 회원 등록 /members/new, /members -> POST
  • 회원 조회 /members/{id} -> GET
  • 회원 수정 폼 /members/{id}/edit -> GET
  • 회원 수정 /members/{id}/edit, /members/{id} -> POST
  • 회원 삭제 /members/{id}/delete -> POST

HTML FORM 특징

  • HTML FORM은 GET, POST만 지원
  • AJAX 같은 기술을 사용하여 해결 가능
  • 컨트롤 URI
    • GET, POST만 지원하므로 제약이 있음
    • 이런 제약을 해결하기 위해 동사로 된 리소스 경로 사용
    • POST의 /new, /edit, /delete가 컨트롤 URI
    • HTTP 메서드로 해결하기 애매한 경우 사용(HTTP API 포함)

태그:

카테고리:

업데이트: