버블 정렬

코드 설계

  • 배열의 왼쪽에서 오른쪽으로 여러번 스캔
  • 스캔 시 인접한 배열의 값과 비교하여 인덱스가 큰 배열의 요소 값보다 인덱스 값이 작은 배열의 요소의 값이 클 경우 위치를 바꾼다.
* 2, 7, 4, 1, 5, 3
1차 -> 2, 4, 1, 5, 3, 7
2차 -> 2, 1, 4, 3, 5, 7
3차 -> 1, 2, 3, 4, 5, 7
// pseudo-code
BubbleSort(A, n) {
    for k <- 1 to n-1 {
        for i <- 0 to n-2 {
            if( A[i] > A[i+1]) {
                swap(A[i], A[i+1])
            }
        }
    }
}
// pseudo-code
BubbleSort(A, n) {
    for k <- 1 to n-1 {
        flag <- 0
        for i <- 0 to n-k {
            if( A[i] > A[i+1]) {
                swap(A[i], A[i+1])
                flag <- 1
            }
        }
        if (flag == 0) break
    }
}

코드 구현

public int[] bubbleSort(int[] A, int n) {
    int[] ret;

    for(int k = 1 ; k < n ; k++) { // 1 ~ n - 1 까지 
        for(int i = 0 ; i < n - 1 ; i++) { // 0부터 n - 2 까지
            if(A[i] > A[i+1]) {

                int tmp = A[i];
                A[i] = A[i+1];
                A[i+1] = tmp;
                // 과정 출력
            }
        }
    }

    ret = A;
    return ret;
}
  • 과정

0:     2    7    4    1    5    3    
1:     2    4    7    1    5    3    
2:     2    4    1    7    5    3    
3:     2    4    1    5    7    3    
4:     2    4    1    5    3    7    

0:     2    4    1    5    3    7    
1:     2    1    4    5    3    7    
2:     2    1    4    5    3    7    
3:     2    1    4    3    5    7    
4:     2    1    4    3    5    7    

0:     1    2    4    3    5    7    
1:     1    2    4    3    5    7    
2:     1    2    3    4    5    7    
3:     1    2    3    4    5    7    
4:     1    2    3    4    5    7    

0:     1    2    3    4    5    7    
1:     1    2    3    4    5    7    
2:     1    2    3    4    5    7    
3:     1    2    3    4    5    7    
4:     1    2    3    4    5    7    

0:     1    2    3    4    5    7    
1:     1    2    3    4    5    7    
2:     1    2    3    4    5    7    
3:     1    2    3    4    5    7    
4:     1    2    3    4    5    7    
  • swap이 없는 경우 loop에서 벗어나는 flag 설정
    public int[] bubbleSort1(int[] A, int n) {
        int[] ret;
        boolean flag = false;

        for(int k = 1 ; k < n ; k++) { // 1 ~ n - 1 까지 
            flag = false;

            for(int i = 0 ; i < n - k ; i++) { // 0부터 n - k 까지
                if(A[i] > A[i+1]) {

                    int tmp = A[i];
                    A[i] = A[i+1];
                    A[i+1] = tmp;

                    flag = true;
                    // 과정 출력
                }
            }
            if(!flag) break;

        }

        ret = A;
        return ret;
    }
  • 과정

1:     2    4    7    1    5    3    
2:     2    4    1    7    5    3    
3:     2    4    1    5    7    3    
4:     2    4    1    5    3    7    

1:     2    1    4    5    3    7    
3:     2    1    4    3    5    7    

0:     1    2    4    3    5    7    
2:     1    2    3    4    5    7
  • 결과 확인 테스트
    int[] A = {2, 7, 4, 1, 5, 3};
    int n = A.length;

    @Test
    public void testBubble() {
        int[] except = {1, 2, 3, 4, 5, 7};
        assertThat(bubbleSort(A, n), is(except));
    }

    @Test
    public void testBubble1() {
        int[] except = {1, 2, 3, 4, 5, 7};
        assertThat(bubbleSort1(A, n), is(except));
    }

분석

  • 복잡도 분석
    • Bast-Case O(n)
    • Average-Case O(n^2)
    • Worst-Case O(n^2)
  • 선택 정렬만큼 우수하지만 마찬가지로 느린 정렬 방식

선택 정렬

  • 가장 간단한 정렬 알고리즘
  • 가장 직관적인 알고리즘

코드 설계하기

  • 목표: 가장 작은 값을 선택하여 자리 바꾸기
    1. 모든 카드를 A에 보관하기
    2. 가장 작은 값의 카드를 선택하여 B에 보관
    3. 2를 반복
// pseudo-code
SelectionSort(A, n) {
    for i <- to n-2 {
        min <- i
        for j <- i+1 to n-1 {
            if(A[j] < A[min]) {
                min <- j
            }
        }
        tmp <- A[i]
        A[i] <- A[min]
        A[min] <- tmp
    }
}

코드 구현

    /**
     * 선택 정렬
     * @param A 정수로 이루어진 배열
     * @param n 배열의 길이
     * @return
     */
    private int[] selectionSort(int[] A, int n) {
        int[] ret;

        for (int i = 0; i < n - 1; i++) { // 0 ~ n-2 까지 반복 (배열의 마지막 전까지)
            int min = i;
            for (int j = i + 1; j < n; j++) { // 1 ~ n-1 까지 반복 (배열의 마지막 까지)
                if (A[j] < A[min]) {
                    min = j;
                }
            }
            int tmp = A[i];
            A[i] = A[min];
            A[min] = tmp;
        }

        ret = A;
        return ret;
    }
  • 과정
0:     2    7    4    1    5    3    
0:     2    7    4    1    5    3    
0:     2    7    4    1    5    3    
0:     2    7    4    1    5    3    
0:     2    7    4    1    5    3    

0:     1    7    4    2    5    3    swap
1:     1    7    4    2    5    3    
1:     1    7    4    2    5    3    
1:     1    7    4    2    5    3    
1:     1    7    4    2    5    3    

1:     1    2    4    7    5    3    swap
2:     1    2    4    7    5    3    
2:     1    2    4    7    5    3    
2:     1    2    4    7    5    3    

2:     1    2    3    7    5    4    swap
3:     1    2    3    7    5    4    
3:     1    2    3    7    5    4    

3:     1    2    3    4    5    7    swap
4:     1    2    3    4    5    7    

4:     1    2    3    4    5    7    

분석

  • 복잡성 분석
    • O(n^2)
  • 선택정렬은 느린 알고리즘에 속함

[Python] Job Scrapper

 * 노마드코더 Python으로 웹 스크래퍼 만들기 강의를 통해 학습하실 수 있습니다.

 

필요한 모듈
모듈명 사이트 설명
requests https://requests.readthedocs.io/en/master/ Python을 위한 간단한 HTTP 라이브러리
BeautifulSoup https://www.crummy.com/software/BeautifulSoup/bs4/doc/ HTML 및 XML 파일에서 데이터를 꺼내기 위한 Python 라이브러리
csv
https://docs.python.org/3.8/library/csv.html  

 

개발 순서
 1. requests를 이용한 URL 호출 (indeed, statck_of_flow)
 2. 검색 키워드, 페이지 파라미터 찾기
 3. URL 호출로 HTML 전문 가져오기
 4. BeautifulSoup을 이용하여 필요한 데이터의 태그 검색하기
 5. 데이터 리스트 만들기
 6. CSV로 내려 받기

 

1 ~ 2. Request
LIMIT = 50
INDEED_URL = f"https://kr.indeed.com/jobs?limit={LIMIT}"
get_request = requests.get(f"{INDEED_URL}&q={keyword}")

 

3. HTML 전문 가져오기
html_parse = bs(get_request.text, 'html.parser')

 

4 ~ 5. 필요한 데이터 태그 검색하기

 - 페이지

 

 

 - job 관련 데이터

 

 

6. CSV 파일로 내려 받기

 

 

Flask를 이용한 Job List Data Crawling

 * 간단한 데이터는 Flask를 이용해 CSV파일로 만들고, 보다 큰 데이터는 Django를 이용해 프로젝트 작성 할 것

'Edu > Nomad Coders' 카테고리의 다른 글

[AWS - Django] 배포하기  (0) 2020.04.09
[Airbnb] 정리  (0) 2020.03.14

 * 포스팅 된 내용은 카카오 OAuth를 사용하여 프로필의 정보를 가져와 서비스 로그인 활용하는 내용을 작성

카카오 로그인 개요

 - REST API를 사용한 카카오 로그인은 HTTP 요청이 가능한 모든 환경에서 쓸 수 있다.

 - REST API를 사용한 카카오 로그인은 카카오 계정 정보를 입력하는 웹뷰(Web View)를 사용

 - 사용자가 로그인을 요청하면 클라이언트(Client)에 카카오 계정 입력 페이지가 나타난다.

 - REST API로 구현하는 카카오 로그인은 인증코드 받기, 사용자 토큰 받기의 두 단계를 거친다.

 - 먼저 인증 코드를 받고 그 인증 코드로 사용자 토큰을 요청하는 방식

 

인증 코드 받기
 - 카카오 로그인을 시작하는 단계
 - 로그인 동의 화면을 호출하고, 인증 코드 발급을 요청하는 API
 - 서비스에서 인증 코드 받기 요청을 하면, 카카오는 인증 코드 발급에 앞서 사용자에게 동의 화면을 보여줌
 - 사용자가 로그인 동의 항목들을 선택하고 '동의하고 시작하기'를 누르면, 카카오는 해당 사용자에 대한 인증 코드를 발급해 서비스에 전달

 

 - 인증 코드 받기 요청 시, 해당 기기에서 카카오계정으로 6시간 이내에 로그인한 적이 있다면 바로 로그인 화면이 나타나거나 로그인된다.
 - 그렇지 않다면, 로그인 창으로 리다이렉트된다.
 - 로그인 화면에서 사용자가 동의 버튼을 클릭하면 인증 코드가 담긴 쿼리 스트링(QueryString)을 요청 시 전달된 redirect_uri로 전송한다.
 - 이때, 응답 상태 유형HTTP 302 Redirect로, Location에 인증 코드 또는 에러 유형을 포함
 - 사용자가 취소 버튼을 클릭하면 에러 쿼리 스트링이 request_uri로 전송된다.

1. 인증 코드 요청

URL

Name  
HOST kauth.kakao.com
URI /oauth/authorize

Parameter

Name Description Required
client_id  - 앱 생성 시 발급 받은 REST API 키 O
redirect_uri  - 코드를 리다이렉트 해줄 URI O
response_type  - code 문자열 값으로 고정 O
state  - 로그인 이전 상태를 유지하기 위해 저장하는 값
 - 결과가 리다이렉트될 때 입력한 state 값이 그대로 전달됨
 - Cross-site Request Forgery 공격을 보호하기 위해 활용 가능
X

 

Response

Name Description
code 사용자 토큰 받기 요청에 필요한 인증 코드

 

Sample

Name URL
redirect_uri http://localhost:8000/users/login/kakao/redirect
request_url https://kauth.kakao.com/oauth/authorize?client_id={client_id}&redirect_uri={redirect_uri}&response_type=code

 

 * 위 작업 시 인증 코드를 활용하여 access_token값을 요청할 수 있는 상태가 된다.


2. 사용자 토큰 받기
 - 인증 코드를 받은 뒤, 인증 코드로 사용자 토큰을 발급 받는 API
 - 사용자 토큰 발급까지 완료되어야 로그인 절차가 끝난다.
 - 필수 파라미터 값들을 담아 POST로 요청
 - 요청 성공 시, 응답은 JSON 객체로 Redirect URI에 전달되며 두 가지 종류의 토큰과 타입, 초 단위로 된 만료 시간을 포함
 - 사용자가 로그인에 성공하면 발급되는 액세스 토큰(Access Token)과 리프레시 토큰(Refresh Token)은 각각 역할과 유효기간이 다르다.
 - 실제 사용자 인증을 맡는 액세스 토큰은 비교적 짧은 만료 시간을 가진다.
 - 하지만 유효한 리프레시 토큰이 있다면, 사용자가 다시 로그인했을 때 리프레시 토큰으로 액세스 토큰을 다시 발급 받을 수 있다.

 

URL

Name  
HOST kauth.kakao.com
POST URI /oauth/token
Content-type application/x-www-form-urlencoded;charset=utf-8

Parameter

Name Description Required
grant_type  - authorization_code로 고정 O
client_id  - 앱 생성 시 발급 받은 REST API 키 O
redirect_uri  - 코드가 리다이렉트 된 URI O
code  - 코드 받기 요청으로 얻은 인증 코드 O
client_secret  - 토큰 발급 시, 보안을 강화하기 위해 추가 확인하는 코드 X

 

Response

Name Description
access_token  - 사용자 액세스 토큰 값
token_type  - 토큰 타입(OAuth 2.0 bearer Authentication)
refresh_token  - 사용자 리프레시 토큰 값
expires_in  - 토큰 만료 시간(초)
scope  - 인증된 사용자의 정보 조회 권한 범위

 

Sample

Name URL
code 인증 코드 요청을 통해 받은 code값
redirect_uri http://localhost:8000/users/login/kakao/redirect
request_url https://kauth.kakao.com/oauth/token?grant_type=authorization_code&client_id={client_id}&redirect_uri={redirect_uri}&code={code}

 

 * access_token을 이용하여 kakao 사용자 관리 API를 호출할 수 있는 상태


3. 사용자 정보 요청
 - 현재 로그인한 사용자의 정보를 불러온다.
 - 사용자 정보 요청 REST API는 사용자 액세스 토큰을 사용하는 방법, 앱 어드민 키를 사용하는 방법 두 가지로 제공된다.
 - 어드민 키는 앱의 마스터 키와 같아 보안에 유의해야 하므로 서버에서 호출할 때만 사용한다.
 - 사용자 액세스 토큰 또는 어드민 키와 사용자 ID를 헤더(header)에 담아 GET/POST로 요청한다.
 - 어드민 키로 요청할 때는 어떤 사용자의 정보가 필요한 지 명시하기 위해 대상 사용자의 ID를 전달한다.
 - 추가 파라미터를 사용하면 특정 정보만 지정해서 받아오거나 URL 응답 값을 https로 받을지 지정할 수 있다.
 - 사용자 정보에는 앱 설정에 등록해 사용하는 추가 사용자 정보 기능이 제공된다.
 - 예로 서비스에서 따로 설정한 사용자 닉네임 등을 카카오 계정 정보에도 저장해 둘 수 있다.
 - 사용자 정보 요청 성공 시, 응답 바디(Body)는 JSON 객체로 사용자 정보들을 포함한다.

 

 * 현재 페이지에서는 Access Token을 사용하는 내용만을 서술, Admin Key를 사용하는 방식은 카카오 공홈 참고

 

URL

Name  
HOST kauth.kakao.com
POST URI /v2/user/me
Authorization Bearer {access_token}
Content-type application/x-www-form-urlencoded;charset=utf-8

Header

Name Description Required
Authorization 헤더 포맷
Authorization: Bearer ${access_token}
O

 

Additional Parameter

Name Type Description
secure_resource Boolean 이미지 URL 값 HTTPS 여부, true 설정 시 HTTPS 사용, 기본 값 false
property_keys JSON Array Property 키 목록, JSON Array를 ["properties.nickname"]과 같은 형식으로 사용

 

Property keys

Name Description
properties.nickname  - 서비스에서 쓰이는 사용자 닉네임
 - 기본 값은 앱연결시의 카카오계정 닉네임
properties.profile_image  - 서비스에서 쓰이는 사용자 프로필 이미지 URL
 - 기본 값은 앱연결시의 카카오계정 프로필 이미지 URL(640* 640)
properties.thumbnail_image  - 서비스에서 쓰이는 사용자 썸네일 이미지 URL
 - 기본 값은 앱 연결 시의 카카오계정 썸네일 프로필 이미지 URL(110* 110)
kakao_account.profile  - 카카오계정의 프로필 소유 여부
 - 실시간 닉네임과 프로필 이미지 URL
kakao_account.email  - 카카오계정의 이메일 소유 여부
 - 이메일 값, 이메일 인증 여부, 이메일 유효 여부
kakao_account.age_range  - 카카오계정의 연령대 소유 여부, 연령대 값
kakao_account.birthday  - 카카오계정의 생일 소유 여부, 생일 값
kakao_account.gender  - 카카오계정의 성별 소유 여부, 성별 값

 

Response (사용자 정보 공통 가이드)

Name Type Description
id Long  - 사용자 ID
kakao_account KakaoAccount  - 카카오계정 정보 (위 링크 참고)
properties JSON  - 추가 정보
synched_at Datetime  - 카카오싱크 간편가입을 통해 로그인한 시각, UTC
connected_at Datetime  - 서비스에 연결 완료된 시각, UTC

 

Sample

Name URL
access_token 사용자 토큰 받기 과정을 통해 받은 access_token
headers Authorization: Bearer {access_token}
request_url https://kapi.kakao.com/v2/user/me

 

 

 * 위 작업을 정상적으로 완료한 경우, 사용자 정보를 통하여 서비스에서 활용할 수 있다.

 

[BoostCourse] Swagger API Document 적용

 - 스프링에 Swagger 적용하는 방법 익히기

Pom.xml spac (Source)

 - spring.version = 4.3.15.RELEASE (5.x.x 버전의 경우 모든 메서드를 구현하지 않아도 됨)

 - jackson.version = 2.9.8

 - swagger.version = 2.9.2

groupId module

version

scope

javax.servlet javax.servlet-api 3.1.0 provided
javax.servlet.jsp javax.servlet.jsp-api 2.3.2-b02 provided
javax.servlet jstl 1.2  
org.springframework spring-webmvc ${spring.version}  
org.springframework spring-test ${spring.version}  
com.fasterxml.jackson.core jackson-core ${jackson.version}  
com.fasterxml.jackson.core jackson-databind ${jackson.version}  
junit junit 4.12 test
io.springfox springfox-swagger2 ${swagger.version}  
io.springfox springfox-swagger-ui ${swagger.version}  

 

config 설정 파일 리스트 (Source)

 1. WebAppInitializer

클래스명 상속 설명
WebAppInitializer AbstractAnnotationConfigDispatcherServletInitializer  
구현 메서드  - getRootConfigClasses()  - Spring 기본 설정파일 클래스를 지정
 - ApplicationConfig.class
 - getServletConfigClasses()  - Spring MVC 설정 파일 클래스를 지정
 - MvcConfig.class, SwaggerConfig.class
 - getServletMappings()  - DispatcherServlet이 동작할 맵핑정보를 설정
 - "/"를 설정한다는 것은 모든 요청을 DispatcherServlet이 처리
 - getServletFilters()  - 인코딩 필터를 설정

 

 2. ApplicationConfig

클래스명 상속 설명
ApplicationConfig @Configuration
@ComponentScan(basePackages = {"service"})
 - Spring MVC에서 사용할 Bean들을 설정하는 스프링 설정

 

 3. MvcConfig

클래스명 상속 설명
MvcConfig @Configuration
@EnableWebMvc
WebMvcConfigurer
@ComponentScan(basePackages = { "controller" })
 
구현 메서드  - configureDefaultServletHandling()  - DefaultServlet에 대한 설정
 - DispatcherServlet이 처리하지 못하는 URL은 DefaultServlet이 처리
 - 해당 설정이 없으면 자동 생성된 Swaager 페이지를 볼 수 없다.

 

4. Swagger

클래스명 상속 설명
Swagger @Configuration
@EnableSwagger2
 
구현 메서드 @Bean
Docket api()
 - Swagger 사용 시에는 Docket Bean 을 품고있는 설정 클래스 1개가 기본으로 필요
 - Spring Boot 에서는 기본적인 설정파일 1개로 Swagger 와 Swagger UI를 함께 사용가능하지만 Spring MVC 의 경우 Swagger UI를 위한 별도의 설정이 필요
 - Swagger UI 를 ResourceHandler 에 수동으로 등록해야 하는 작업인데, SpringBoot 에서는 이를 자동으로 설정해주지만 Spring MVC 에서는 그렇지 않기 때문이다.

 

Controller, Service, Dto

 - Controller (Source)

클래스명 Swagger Annotation
CalculatorApiController  
CalculatorResult plus(value1, value2) @ApiOperation(value = "덧셈 구하기") 
@ApiResponses({  
            @ApiResponse(code = 200, message = "OK"), 
            @ApiResponse(code = 500, message = "Exception") 
})
CalculatorResult minus(value1, value2)
@ApiOperation(value = "뺄셈 구하기")
@ApiResponses({  // Response Message에 대한 Swagger 설명
            @ApiResponse(code = 200, message = "OK"),
            @ApiResponse(code = 500, message = "Exception")
})

 

Swagger Page

 - 설정 후 http://localhost:8088/swagger-ui.html로 접근 시 Swagger UI로 확인 가능

 

 * AWS 공식 홈페이지 기반으로 작성하는 Django 배포하기

EB CLI 설치
1. pipenv 환경에 AWS EB CLI 설치 (Link)
 - EB 명령어 (Link)
 - EB 오류관련 (Link)

 * pipenv install awsebcli --dev
 * pathspec 버전 오류가 발생 할 수 있으나 그냥 진행
AWS Elastic Beanstalk Django 배포
1. AWS Elastic Beanstalk 개념 (Link}
2. Elastic Beanstalk 사용 시작하기 (Link)
3. Elastic Beanstalk 파이썬 플랫폼 사용하기 (Link)
4. Django 어플리케이션을 Elastic Beanstalk에 배포하기 (Link)
 - Elastic Beanstalk에 대한 Django 애플리케이션 구성
 * pip freeze > requirements.txt
  > 애플리케이션의 모듈을 로드할 수 있도록 환경 변수를 설정
 * .ebextensions/django.config
  > 어플리케이션 실행 Elastic Beanstalk가 애플리케이션을 시작하는 데 사용하는 WSGI 스크립트의 위치를 지정

 - IAM User 생성
 # access_id, access_secret

 - EB CLI를 사용하여 사이트 배포
 * eb init (Link)
  > 프로젝트 디렉터리 및 EB CLI를 구성할 준비
 * eb create app_name
  > eb create로 해당 환경에 애플리케이션을 배포
 * eb deploy
  > EB CLI가 프로젝트 디렉터리의 콘텐츠를 번들링한 후 이를 환경에 배포

5. Elastic Beanstalk에 첫 Django 애플리케이션을 배포

 

'Edu > Nomad Coders' 카테고리의 다른 글

[Python] Data Crawling  (0) 2020.04.19
[Airbnb] 정리  (0) 2020.03.14
이 글은 노마드코더[풀스택] 에어비엔비 클론 코딩 학습한 뒤 정리중인 내용입니다.
 * 이 글은 강의 핵심 내용외에 참고하기 위한 일부분입니다. 

 

Contents
1. Airbnb ERD
2. Django Project Structure
3. Login Process
4. Host Process
5. Guest Process
6. Reservation Process
7. URL Management
8. AWS Architecture

 

Airbnb ERD
# Airbnb_ERD

 

Django Project Structure
 - URL Mapping
URL Mapping
Login Process

 1. Email Login

 

 2. Github Login

 

 3. Kakao Login

OAuth - Kakao

 

Host Process

 

Guest Process

 

Reservation Process

 

AWS Architecture

 1. EB CLI 설치

 2. EB 구성하기

 3. eb init

 4. Django framework에 Elastic BeanStalk 설정

 5. EB에 Django 설치

 6. Postgresql 설정

 7. Sentry 설치

 8. statics files 업로드를 위한 S3 설정

Graduating From [풀스택] 에어비앤비 클론 코딩

'Edu > Nomad Coders' 카테고리의 다른 글

[Python] Data Crawling  (0) 2020.04.19
[AWS - Django] 배포하기  (0) 2020.04.09
Dynamic Web Project 3.1 spec

 - Servlet 클래스 작성 시 web.xml 변화 확인 3.x spec부터는 annotation으로 설정

 - 프로젝트 생성

 - 다음 페이지

 - 다음 페이지 web.xml 생성

 - 프로젝트 구조 확인

 - web.xml 확인

 - Servlet 클래스 작성

 - URL Mapping 수정

 - 메서드 생성

 - 생성된 서블릿 확인 @WebServlet("URL")이 생기는 점이 2.5 spec과 3.x spec의 차이점
(web.xml에 Servlet 등록 태그로 설정하지 않고 annotation으로 설정)

 - Servlet 생성 후 web.xml 확인 ( 변화없음 )

 

+ Recent posts