설치할것 3주차

 

 

 

파이썬 설치하기

https://www.python.org/downloads/

download python 3.8.x 버튼 눌러서 설치하기.

프로그램 실행

add python 3.8 to path 를 꼭 체크해줘야 함.

파이썬 설치할 때 32비트던데, 64비트는 왜 안씀?

 

 

 

몽고디비 설치하기

c드라이브에 /data/db 라고 디렉토리 만들기.

https://www.mongodb.com/try/download/community

몽고디비 커뮤니티 서버를 받아야 하며, 운영체제는 내 운영체제에 맞게 선택하고 다운.

 

설치시 커스텀을 선택하고,

 

위치만 아까 만든 디렉토리로 바꾼다.

 

여긴 기본값 그대로 넘어간다.

 

컴퍼스는설치하지 않는다.

 

 

 

이런 메시지 뜨면 ignore

 

다끝나면 리스타트하라고 보내느데 일단 나중에.

 

 

환경 변수설정을 위해 다음을 들어간다.

 

여기서 환경변수를 누른다.

 

 

시스템 변수 (사용자변수 아님)에 path를 찾아 편집을 누른다.

 

 

오른쪽상단에 새로만들기 누르고, 아까 몽고디비가 깔린 경로에 bin까지 들어간 경로를 넣어준다.

 

 

완료.

 

몽고디비 시작

윈도우 cmd 에서 mongod 치면 실행됨.

 

 

이런식으로 나옴. (끄면 안될 것 같은데, 원격강사는 꺼도 된다고 함;; 끄고 한번 해보자)

 

브라우저 가서,

localhost:27017 쳐서 다음과 같이 나오면 잘 실행되고 있다는 것임

 

 

 

 

 

robo3t 설치

https://robomongo.org/download

오른쪽 거 받아야 함.

 

상단 exe 선택

 

이건 그냥 다음다음다음 쭉쭉쭉 하면 설치하면 됨.

 

 

 

 

 

 

 

git bash 설치하기 (윈도우만)

 

https://tnsgud.tistory.com/648 참고

 

https://git-scm.com/ 에 들어가서,

다운로드 선택

 

 

 

64비트 git for windows setup 선택

설치중 선택할 게 엄청 많은데, 싹 다 next로 넘어감.

 

 

 

=========================================================

2주차 복습

 

스파르타가 만든 영화 가져오는 openapi 사용하기

api 주소 : API주소(GET 요청) → http://spartacodingclub.shop/post

빈 메모장에, 해당 api를 통해 아래처럼 영화를 가져오기.

나홀로 메모장에 들어가는 **아티클들의 정보를 불러오는 OpenAPI**입니다. 
서버에서 JSON 형식으로 아티클 정보(articles)를 응답(response) 데이터로 보내주죠!
이 API를 써서 '저장된 포스팅 불러오기' 기능을 만들어볼게요! 
(나중에 이 API를 우리가 직접 만들어 볼겁니다)

 

 

 

api 훑어보기

값이 있고 그 아래 articles 라는 리스트 안에 딕셔너리 자료들이 있다. 딕셔너리는 형태가 동일하다.

comment, desc, image, title, url 

 

참고

크롬에서 주소 입력해서 엔터 = get요청 = ajax get 요청 모두 동일

 

 

일단 홈페이지를 들어가면 바로 api를 접근해서 데이터를 받아오는 식으로 할것임.

그러므로 ready 함수가 필요하다.

$(document).ready(function(){
  showPost();
});

즉, html에 영화내용은 없다가, html에 접속하면 showPost()함수가 시작하여 영화내용 데이터 카드들을 가져오는것.

 

function showPost() {
	$.ajax({
	  type: "GET",
	  url: "http://spartacodingclub.shop/post",
	  data: {},
	  success: function(response){
			console.log(response);
	  }
	});
}

이렇게 하면, response에 저 json값들이 다 들어가므로 response['article'][i]['comment'] 이런식으로 접근할 수 있음.

 

이제 ajax를 수정한다. 카드를 가져오도록.

즉  따로 카드(html)을 만드는 함수를 만들고 ajax에서 그 함수를 돌려서 카드를 해당 api내용만큼 만드는 식으로 하고, 그 값을 ready함수에 넣고.. 이런식으로 최종적으로 만들 수 있다.

 

완료

<!Doctype html>
<html lang="ko">

<head>
    <!-- Required meta tags -->
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

    <!-- Bootstrap CSS -->
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css"
          integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">

    <!-- JS -->
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js"
            integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q"
            crossorigin="anonymous"></script>

    <!-- 구글폰트 -->
    <link href="https://fonts.googleapis.com/css?family=Stylish&display=swap" rel="stylesheet">


    <title>스파르타코딩클럽 | 나홀로 메모장</title>

    <!-- style -->
    <style type="text/css">
        * {
            font-family: 'Stylish', sans-serif;
        }

        .wrap {
            width: 900px;
            margin: auto;
        }

        .comment {
            color: blue;
            font-weight: bold;
        }

        #post-box {
            width: 500px;
            margin: 20px auto;
            padding: 50px;
            border: black solid;
            border-radius: 5px;
        }
    </style>
    <script>
        function openClose() {
            // id 값 post-box의 display 값이 block 이면(= 눈에 보이면)
            if ($("#post-box").css("display") == "block") {
                // post-box를 가리고
                $("#post-box").hide();
                // 다시 버튼을 클릭하면, 박스 열기를 할 수 있게 텍스트 바꿔두기
                $("#btn-post-box").text('포스팅 박스 열기');
            } else {
                // 아니면(눈에 보이지 않으면) post-box를 펴라
                $("#post-box").show();
                // 다시 버튼을 클릭하면, 박스 닫기를 할 수 있게 텍스트 바꿔두기
                $("#btn-post-box").text("포스팅 박스 닫기");
            }
        }
        $(document).ready(function () {
            $("#cards-box").empty();  // 일단 비우고 (원래 밑에 있는 데이터때문)
            showPost(); // 자동으로 아래 포스트를 실행한다. 브라우저 새로고침하거나 들어갈 때
        });
        function showPost() {
            $.ajax({
                type: "GET",
                url: "http://spartacodingclub.shop/post",
                data: {},
                success: function (response) {
                    let articles = response['articles'];
                    for (let i = 0; i < articles.length; i++) {
                        let article = articles[i];
                        makeCard(article["image"], article["url"], article["title"], article["desc"], article["comment"]);
                    }
                }
            });
        }
        function makeCard(image, url, title, desc, comment) {
            let tempHtml = `<div class="card">
                                <img class="card-img-top" src="${image}" alt="Card image cap">
                                <div class="card-body">
                                    <a href="${url}" target="_blank" class="card-title">${title}</a>
                                    <p class="card-text">${desc}</p>
                                    <p class="card-text comment">${comment}</p>
                                </div>
                            </div>`;
            $("#cards-box").append(tempHtml);
        }
    </script>

</head>

<body>
<div class="wrap">
    <div class="jumbotron">
        <h1 class="display-4">나홀로 링크 메모장!</h1>
        <p class="lead">중요한 링크를 저장해두고, 나중에 볼 수 있는 공간입니다</p>
        <hr class="my-4">
        <p class="lead">
            <button onclick="openClose()" id="btn-post-box" type="button" class="btn btn-primary">포스팅 박스 열기</button>
        </p>
    </div>
    <div id="post-box" class="form-post" style="display:none">
        <div>
            <div class="form-group">
                <label for="post-url">아티클 URL</label>
                <input id="post-url" class="form-control" placeholder="">
            </div>
            <div class="form-group">
                <label for="post-comment">간단 코멘트</label>
                <textarea class="form-control" rows="2"></textarea>
            </div>
            <button type="button" class="btn btn-primary">기사저장</button>
        </div>
    </div>
    <div id="cards-box" class="card-columns">
        <div class="card">
            <img class="card-img-top"
                 src="https://www.eurail.com/content/dam/images/eurail/Italy%20OCP%20Promo%20Block.adaptive.767.1535627244182.jpg"
                 alt="Card image cap">
            <div class="card-body">
                <a href="#" class="card-title">여기 기사 제목이 들어가죠</a>
                <p class="card-text">기사의 요약 내용이 들어갑니다. 동해물과 백두산이 마르고 닳도록 하느님이 보우하사 우리나라만세 무궁화 삼천리 화려강산...</p>
                <p class="card-text comment">여기에 코멘트가 들어갑니다.</p>
            </div>
        </div>
        <div class="card">
            <img class="card-img-top"
                 src="https://www.eurail.com/content/dam/images/eurail/Italy%20OCP%20Promo%20Block.adaptive.767.1535627244182.jpg"
                 alt="Card image cap">
            <div class="card-body">
                <a href="#" class="card-title">여기 기사 제목이 들어가죠</a>
                <p class="card-text">기사의 요약 내용이 들어갑니다. 동해물과 백두산이 마르고 닳도록 하느님이 보우하사 우리나라만세 무궁화 삼천리 화려강산...</p>
                <p class="card-text comment">여기에 코멘트가 들어갑니다.</p>
            </div>
        </div>
        <div class="card">
            <img class="card-img-top"
                 src="https://www.eurail.com/content/dam/images/eurail/Italy%20OCP%20Promo%20Block.adaptive.767.1535627244182.jpg"
                 alt="Card image cap">
            <div class="card-body">
                <a href="#" class="card-title">여기 기사 제목이 들어가죠</a>
                <p class="card-text">기사의 요약 내용이 들어갑니다. 동해물과 백두산이 마르고 닳도록 하느님이 보우하사 우리나라만세 무궁화 삼천리 화려강산...</p>
                <p class="card-text comment">여기에 코멘트가 들어갑니다.</p>
            </div>
        </div>
        <div class="card">
            <img class="card-img-top"
                 src="https://www.eurail.com/content/dam/images/eurail/Italy%20OCP%20Promo%20Block.adaptive.767.1535627244182.jpg"
                 alt="Card image cap">
            <div class="card-body">
                <a href="#" class="card-title">여기 기사 제목이 들어가죠</a>
                <p class="card-text">기사의 요약 내용이 들어갑니다. 동해물과 백두산이 마르고 닳도록 하느님이 보우하사 우리나라만세 무궁화 삼천리 화려강산...</p>
                <p class="card-text comment">여기에 코멘트가 들어갑니다.</p>
            </div>
        </div>
        <div class="card">
            <img class="card-img-top"
                 src="https://www.eurail.com/content/dam/images/eurail/Italy%20OCP%20Promo%20Block.adaptive.767.1535627244182.jpg"
                 alt="Card image cap">
            <div class="card-body">
                <a href="#" class="card-title">여기 기사 제목이 들어가죠</a>
                <p class="card-text">기사의 요약 내용이 들어갑니다. 동해물과 백두산이 마르고 닳도록 하느님이 보우하사 우리나라만세 무궁화 삼천리 화려강산...</p>
                <p class="card-text comment">여기에 코멘트가 들어갑니다.</p>
            </div>
        </div>
        <div class="card">
            <img class="card-img-top"
                 src="https://www.eurail.com/content/dam/images/eurail/Italy%20OCP%20Promo%20Block.adaptive.767.1535627244182.jpg"
                 alt="Card image cap">
            <div class="card-body">
                <a href="#" class="card-title">여기 기사 제목이 들어가죠</a>
                <p class="card-text">기사의 요약 내용이 들어갑니다. 동해물과 백두산이 마르고 닳도록 하느님이 보우하사 우리나라만세 무궁화 삼천리 화려강산...</p>
                <p class="card-text comment">여기에 코멘트가 들어갑니다.</p>
            </div>
        </div>
    </div>
</div>
</body>

</html>

 

완료===========================================

 

이제 3주차는 직접 서버를 구축하는 쪽의 개념임

 

 

 

 

파이썬 시작

 

 

파이썬을 설치한다는 것은, 일종의 번역팩, 즉 컴퓨터는 2진수만 쓰는데, 파이썬문법으로 된 것을 컴퓨터가 읽을 수 있도록 번역해주는 번역기를 설치하고, 프로그래밍을 돕는 기본코드오 ㅏ함수들을 설치하는것.

파이썬은 매우 직관적인 언어이고, 할 수 있는 것도 많습니다. 그런데, 개발자들도 모든 문법을 기억하기란 쉽지 않습니다. 오늘 배우는 것 외에 필요한 것들은 구글링해서 찾아보면 됩니다. (추천 검색어 : python 찾고자_하는_기능)

 

또한 파이썬은 할 수있는것도 많고 모든 문법을 개발자가 기억하고 있는것도 아님.

여기서 배우는 것외에 필요한 것들은 구글링하면 된다.

추천 검색어 : python 찾고자_하는_기능

 

 

 

 

만약 configure python interpreter.. 이런식으로 나오는 경우,

 

 

파이썬에서 결과 바로바로 보는 콘솔 열기

 

 

파이참에서 파이썬 구문 바로 실행하기 : ctrl+shift+f10 

 

 

파이썬도 보통 프로그래밍언어처럼 변수, 자료형(리스트, 딕셔너리등), 함수, 조건, 반복문 정도를 이해하면 된다.

파이썬에서 이름지을때는 보통 snake style을 사용함. 변수는 명사형, 함수는 동사형

 

파이썬 공식 스타일 가이드

pep8 : https://www.python.org/dev/peps/pep-0008/

파이썬자습서-코딩스타일https://docs.python.org/ko/3/tutorial/controlflow.html#intermezzo-coding-style

 

 

 

 

파이썬의 특징

리스트 안에 딕셔너리가 있는데, 들어간 것과 다른 딕셔너리 혁식의 딕셔너리를 넣었음. 그럼 잘 들어감. 딱히 문젝 없고 매우 유연함. 아무거나 다 넣을 수 있어서 좋음 그래서 일관성을 잘 만들어야 하며, 문제가 있어도 모를 수 있음.

자바스크립트는 중괄호로 여러가질 나눴는데, 파이썬은 중괄호가 없음. 들여쓰기로 구분한다. 들여쓰기 안맞으면 에러남.

함수, if 같은 정의를 쓸 때 끝에 : 를 붙인다.

 

return은 해당 조건이 맞으면 바로 끝난다.

for 문에다가 특정 조건을 주고 return을 하면, 그 조건에 맞으면 for문은 그냥 끗.

 

반복문, 조건문에서 중간에 끝낼라면 리턴을 넣으면 됨.

 


프로그래밍언어를 다 외울려고 하지 말자.어차피 못한다. 우린 다른것도 해야 하잖아.

 

 

파이썬 변수와 자료형

 


a = 3
name = "uktaekim"
print(a+name)
이건 에러남. int와 str은 + 연산이 안됨.


파이썬 에러메시지 뜨면 꼭 읽어보자 에러메시지 뜨면, 에러메시지 제대로 복사해서 구글링해보자. 잘 나온다.

 

 

 

 

파이썬 함수

 

 

조건문

형식이 조금 다를 뿐, 논리는 같다.

if 하고 조건을 준 후 : 로 마무리한다.

그게 맞다면, 아래 구문을 넣고, 아니라면(else) else 밑에 구문을 넣는다.

 

반복문

자바스크립트에서 반복문은 반복할 범위도 직접 정해줘서 length함수를 썼었는데, 파이썬에서 반복문은 일단 기본적으로 임의의 열 (sequence, 리스트나 문자열처럼) 의 항목을 그냥 그 순서대로 하나씩 끝까지 꺼내서 반복한다.

그래서 리스트를 많이 쓴다.

 

 


자바스크립트에서는 반복문을 보통 리스트에서 리스트의 사이즈를 정의하고 꺼냈는데,
파이썬에서는 쿨하게 그냥 리스트에 있는것들을 하나 쓰는걸 반복문이다! 라고 정의해버림.

fruits = ['사과', '감', '배', 
for p in fruits:
p(fruit)
이렇게하면, fruits 배열에 있는 내용 하나하나를 꺼내서 fruit에 넣은다음, 아래 print(fruit)를 실행함.
p는 뭐라고 써도 상관없고, 보통 p를 단수값, in 뒤에 배열은 복수값으로 많이 한다고 함.
for fruit in fruits 이런식.

 

 



개수세기가 있다면, 개수를 넣을 변수가 필요함. 그래서 보통 count 라고 for 전에 씀.

count = 0
for fruit in fruits:
내용쓰고 ㅁ지막에
count += 1 이런식으로 넣음.
이런 폼을 기억하자.


def count_fruits(name):
    count = 0
    for fruit in fruits:
        if fruit == name:
            count += 1
    return count
여기에서, for를 count+=1 까지 계속 돌려서 count를 증가시키고, for문이 끝나면 (fruits배열의 끝까지 가면,) count를 return 하는것임.

 

 

 

 

 

파이썬 패키지와 라이브러리

누가 구현한 파이선의 함수, 모듈등을 모아놓은 것. 이러한 패키지를 모아놓은것이 라이브러리이다.

여기서는, 라이브러리를 사용하기 위해 어떤 패키지를 설치하는 것.

 

 

가상환경이란?

회사프로젝트에서 패키지a,b,c를 쓴다.

개인프로젝트에서는 패키지 b,c,d,e를 쓴다.

근데 회사에서 패키지b+를 쓰라고 함;;

그럼 개인프로젝트ㄱ가 난감해짐.

패키지는 다 깔아놓고, 회사프로젝트는 abc를 쓰고, 개인프로젝트는 bcde 를 쓰도록 해서, 프로젝트별 가상환경을 만드는 것.

 

정리하면, 가상환경은 같은 시스템에서 실행되는 다른 파이썬 응용 프로그램들의 동작에 영향을 주지 않기 위해 파이썬 배포 패키지들을 설치하거나 업그레이드하는 것을 가능하게 하는 격리된 실행환경임.

공식설명

https://docs.python.org/ko/3/glossary.html#term-virtual-environment

 

라이브러리를 여러개 까는건 문제없다.

이런 가상환경은 별거 없고 그냥 폴더이다. 아래 그림에서 venv 라는 디렉토리 (virtual environment) 

사용하는 패키지들이 거기 들어간다고 생각하면 됨. 여기에서 패키지를 읽어오므로, 다른 프로젝트와 문제생길 일이 없음.

 

가상환경 설치하기

 


어떤 파이썬을 쓸 지 선택

confiture python interpreter -> add interpriter 하고 그냥 ok 하면, 파이참 왼쪽에 venv 디렉토리가 생김. 이게 가상 그건가봐.
venv는 우리눈에 보이지만, 안보이는거로 생각하면됨. 여기 밑에 파일 절대 만들지마라.

 

더 추가하여 자세한건 아래 내용 참고

https://dojang.io/mod/page/view.php?id=2470

 

 

 

 

pip 사용하여 패키지 설치하기

앱을 설치할 때 앱스토어를 가듯이 새로운 프로젝트의 라이브러리를 가상환경에 설치하려면 pip을 이용한다.

file -> setting -> project interpreter에 들어가서, 오른쪽 더하기 (아래에 있을수도 있음) 를 누름.

이런 패키지들은 패키지/라이브러리의 홈페이지 가면 사용법과 예제코드 들 있음. 아래에서 패키지검색할 때 사이트가 나옴.

 

그리고 여기 오른쪽 기어모양 누르면 가상 공간 관련하여 세팅할 수 있음.

 

 

 

라이브러리를 사용하는 기초 연습 : 서울시 openapi 사용하기

 

서울시 권역별 대기현황

api 문서 : https://data.seoul.go.kr/dataList/OA-2219/A/1/datasetView.do;jsessionid=7106F33493DD3E59FEEB87B59FDEA12F.new_portal-svr-11

api : http://openapi.seoul.go.kr:8088/6d4d776b466c656533356a4b4b5872/json/RealtimeCityAir/1/99

 

딱 중구의 NO2 값만 가져오기

# requests 라이브러리 설치
import requests

# requests 를 사용해 요청(Request)하기
response_data = requests.get('http://openapi.seoul.go.kr:8088/6d4d776b466c656533356a4b4b5872/json/RealtimeCityAir/1/99')

# 응답(response) 데이터인 json을 쉽게 접근할 수 있게 만들어 city_air 에 담고
city_air = response_data.json()

# 값을 출력
print(city_air['RealtimeCityAir']['row'][0]['NO2'])

 

requests는 명시된 경로의 데이터를 가져오는 것이며 거기 get()을 써서 가져옴. 

이미구현되어있는 json() 이라는 함수를 사용한다. json함수는 받은 값을 json형태로 구현하는 것임.

json()으로 넘어온 데이터를 접근가능하게 .json()으로 바꿔준 것 뿐임 -> 온라인강사가 한 얘기. 서로 다르니깐 찾아바ㅗ야함.

json()을 안쓰면, 값이 안나온다.

여기서 json 함수와 get 함수는 requests를 임포트했기때문에 사용가능한것임.

이걸 두줄로 만들어버리면 아래처럼 된다.

import requests

print(requests.get('http://openapi.seoul.go.kr:8088/6d4d776b466c656533356a4b4b5872/json/RealtimeCityAir/1/99').json()['RealtimeCityAir']['row'][1]['NO2'])

 

 

다시정리

데이터를 가져올 때 다음과 같이 데이터 자체가 여기 딕셔너리부터 시작하는것임.

이 빨간게 request.get해서 받은 값이고 그걸 변수에 넣고 이제 ['RealtimeCityAir'] 부터 시작하는거

 

 

PM10 값이 20미만인 구만 출력하기

gu_infos = city_air['RealtimeCityAir']['row']

for gu_info in gu_infos:
    print(gu_info['MSRSTE_NM'], gu_info['PM10'])

 

 

 

 

 

웹 스크래핑 

 

 

스크래핑이란, 한국에선 크롤링이라고 하기도 하며, 웹페이지에서 우리가 원하는 부분의 데이터를 수집하는 것임. 

 

크롤링 : 검색엔진이 웹사이트를 가져오는거

스크래핑 : 사이트에서 데이터 추출하는거

 

구글로 더 검색할때는 web scraping으로 검색해야 자세한 정보가 나옴.

스크래핑 할때마다 서버에 붙는게 아니다. 일단 html을 받고, (즉 접속하고,) 다운받아진 html에서 데이터를 추출하는것이다. 즉 받은 html은 우리꺼니까 서버 부하는 없다.

 

참고

[Web Scarping](https://en.wikipedia.org/wiki/Web_scraping)(wikipedia)

[Web Crawler](https://en.wikipedia.org/wiki/Web_crawler)(wikipedia)

[Web Scraping vs Web Crawling: What’s the Difference?](https://dzone.com/articles/web-scraping-vs-web-crawling-whats-the-difference)

 

 

 

관련 패키지 다운

beautifulsoup4 = html 코드를 쉽게 스크래핑해오기 위한 도구

requests 패키지 설치한것처럼 설치하자.

beautifulsoup4 가 bs4인듯함. 이 안에 여러가지 패키지가 있다.

bs4라고 해서 설치해도 됨. 같은거라고 한다.

 

import를 할 때, bs4를 전체를 임포트해도 되는데, 여기 스크래핑할 때 다 임포트할필요는 없다. 필요한게 BeautifulSoup이거든.

그래서 아래와 같이 from을 써서, bs4 에서 beautifulSoup만 임포트할 수 있다.

beautifulSoup만 임포트한다고 해서 from bs4를 빼면 당연히 안됨.

import requests
from bs4 import BeautifulSoup

 

 

 

 

 

 

네이버 영화 스크래핑하기

 

import requests
from bs4 import BeautifulSoup

headers = {'User-Agent' : 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36'}
data = requests.get('https://movie.naver.com/movie/sdb/rank/rmovie.nhn?sel=pnt&date=20200716',headers=headers)

soup = BeautifulSoup(data.text, 'html.parser')

#############################
# (입맛에 맞게 코딩)
#############################

 

설명

requests 패키지 import 수행 : request.get 함수를 사용하기 위함

bs4 패키지에서 BeautifulSoup 패키지 import 수행 : BeautifulSoup 함수 사용하기 위함

text 함수 : 태그 안에 있는 글자 <a>안녕</a> 여기서 안녕을 가져온다.

 

requests.get 함수

이 함수는 명시한 경로의 html 통데이터를 다 가져온다. json이 아니고 그냥 raw 데이터를 가져와버린다. ajax콜을 수행하며 아주 쉽게 할 수 있다.

즉 2개의 값을 받는다. (더받을수도 있음. 여긴 일단 2개값만) html 데이터를 가져올 주소, 헤더정보를 넣어주며, 이 함수의 결과를 data 변수에 넣었다.

 

 

우리가 브라우저로 접속하는거랑, requests로 땡겨오는것은 조금 차이가 있다. 보통 헤더에서 차이가 나는데, 헤더를 넣는것은 우리가 마치 브라우저에서 엔터친것처럼 강제로 넣어준것임.

 

헤더 정보는 접속하는 클라이언트의 정보를 명시하는 것이며, headers 변수에 넣었고 그 변수를 requests.get 함수에 넣었다. 사실 이 헤더정보는 없어도 되고 있어도 된다. 하지만 있는게 좋다. 왜냐면 실제로 인터넷에는 자동 웹 스크래핑 봇이 엄청 많으며, 봇 때문에 어떤 서버는 헤더정보를 주지 않으면 봇이 접근한다고 생각해서 데이터를 가져가지 못하게 막는 경우가 있다. 그래서 헤더를 사용한다. 근데 봇도 헤더를 쓸 수 있다고 한다.. 뭐 어쩌라는건지 잘 모르겠다. 아래 내용도 참고할 것.

https://m.blog.naver.com/PostView.nhn?blogId=kiddwannabe&logNo=221185808375&proxyReferer=https:%2F%2Fwww.google.com%2F

 

 

BeautifulSoup 함수

html 내용에 이 함수를 적용하여 검색하기 용이한 상태로 만들어준다. 즉 예쁘게 만들어준다. 일단 이 함수도 2개의 값을 받는데, 아까 네이버영화의 html 을 넣은 data 변수에 text 함수를 적용한 값과, 예쁘게 만드는 파싱 이라는 걸 수행하도록 어떤 파서를 쓸 지 명시한다. 결과는 soup 변수에 담긴다.

 

테스트로 soup 변수를 출력해보면, 다음과 같이 나온다. print(soup)

 

이렇게 태그가 예쁘게 나온다. 이제 원하는 값을 찾는 방법을 알아보자.

 

 

text함수와 json()함수 문의. 

text 함수는 태그 안의 텍스트를 가져온다. 태그.text

 

 

원하는 값을 찾기 : 셀렉터

아까 네이버 영화에서, 그린 북 이라는 글자를 한번 찾아보자.

크롬에서 그린북 글자를 오른클릭하고, 검사 누른다음 표시된 html부분을 오른클릭하고 copy->copy selector를 선택한다.

 

복사된 것을 붙여넣어보면 이런 결과가 나온다.

#old_content > table > tbody > tr:nth-child(2) > td.title > div > a

이 내용을 잘 보면, 맨 앞의 #old_content는 선택자이며, table, tbody, tr, td, div, a 등은 태그이다.

따라서 그린북이라는 내용이 있는 하나의 경로를 출력한 것이다. "위와 같은 순서를 통해 "그린북" 이라는 글자 위치가 있다." 뭐 이런 뜻.

 

근데 왜 #old_content 부터일까? 실제로 해당 html 사이트의 최상위부터 그린북까지 쭉 태그를 가져와야 하지 않나? 라는 의문이 있을 수 있다. 다음 그림을 보자.

이 그림은 해당 사이트의 <body> 부분부터 그린 북 글자가 있는 <a> 태그까지의 모든 태그의 경로를 보여준다. 근데 실제로 copy selector를 했을 때는 중간에 <div>가 있는 id인 old_content 부터이다.

 

그림처럼 싹 다 나와야 할 것 같은데, 그렇지 않고 old_content 부터 나오는 이유는, 해당 html에 old_content라는 선택자(id)가 있고 딱 하나만 있어서 이값으로 바로 액세스할 수 있기 때문이다. 만약 old_content라는 값이 여러개 있었다면, 좀 더 상위로 가야 했을수도 있다. 어쨌든 하나의 경로로 인식해야 하기 때문.

 

아무튼 핵심은, 내가 가져오고 싶은 그린북이라는 글자가 있는 위치를 selector를 통해 다음과 같이 특정할 수 있다는 것이 중요하다.

#old_content > table > tbody > tr:nth-child(2) > td.title > div > a

 

추가로, 위와 같이 글씨 말고 다른 형식이라면? 예를들어 아까 순위에서 그린북이 1위였는데, 1이라는 글자? 또는 그림? 암튼 이걸 가져오고 싶다면?

 

여기서 보면 1이라는 부분이 img이다. 따라서 이미지라서 좀 어려운데, 보니 alt=01이 있다. 이건 개발자가 이미지가 깨지는 경우 숫자로라도 01이라고 나오게 하라는 것이다. 이 alt로 하면 되겠네!

 

이걸 copy selector로 복사해보니 아래와 같이 나온다.

#old_content > table > tbody > tr:nth-child(2) > td:nth-child(1) > img

img 태그에 해당 01이 있고, img 태그의 속성 중 alt가 01인 것이다.

 

 

# 선택자를 사용하는 방법 (copy selector)

위 방식처럼 #아이디를 사용해서 선택자를 사용했다면, 그 외에 다른방법도 더 있다.

아래 방법은 구글링을 좀 해야할 것 같다. 볼드체는 해본것이며, 볼드 아닌것은 찾아보자.

일단 구글 검사에서 copy selector를 하는것은 동일하며, 태그, 클래스 등도 선택자로 사용할 수 있다는 거.

 

soup.select('태그명')

soup.select('.클래스명')

soup.select('#아이디명')

 

soup.select('상위태그명 > 하위태그명 > 하위태그명')

soup.select('상위태그명.클래스명 > 하위태그명.클래스명')

 

# 태그와 속성값으로 찾는 방법

soup.select('태그명[속성="값"]')

 

 

 

 

여기까지 확인할 줄 안다면 이제 코딩을 하기 위한 기본 준비가 된 것이다. 이제 어떻게 출력할지를 코딩하면 된다. 아래 예제를 해보자.

 

 

 

 

사전 함수 이해

변수.select_one 함수 : 태그 하나만 가져온다.

text 함수 : 태그가 감싸고 있는 글자를 가져온다.

select는 해당 태그를 모두 리스트로 가져옴.

beautifulsoup는 select랑, select_one만 잘 알면 된다.

 

 

예제 : 영화 순위 가져오기

아까 확인했던 영화 순위를 가져와보자.

 

 

구문

import requests
from bs4 import BeautifulSoup

headers = {'User-Agent' : 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36'}
data = requests.get('https://movie.naver.com/movie/sdb/rank/rmovie.nhn?sel=pnt&date=20200716',headers=headers)

soup = BeautifulSoup(data.text, 'html.parser')

movieRank=soup.select_one('#old_content>table>tbody>tr:nth-child(2)>td:nth-child(1)>img')
print(movieRank['alt'])

설명

soup에 해당 영화 사이트 html이 들어가있고, soup에 select_one 라는 함수를 적용하고, 인자값으로 아까 확인했던 img 태그까지의 경로를 지정해주면, html 사이트에서 #old_content부터 시작해서 img를 찾아낼 수 있게 되고, 그 값을 movieRank 변수에 넣는다. 그 변수의 결과는 다음과 같다.

이렇게 딱 <img> 태그 전체가 나오는데, 이 값 중 alt에 있는 01을 최종적으로 가져와야 한다. 그래서 movieRank변수에 딕셔너리처럼['alt']라는 항목을 가리킬 수 있다. alt : 01 이런식의 딕셔너리라고 생각하면 된다. 이렇게 지정해서 01이라는 결과를 확인했다.

 

결과

여기서는 운이 좋게 01을 가져올수 있었다. 왜냐면 alt값에 개발자가 01로 했기 때문. 만약 alt 값이 없었다면, 어떻게 해야 할지 모르겠지만, src 항목의 이미지 경로를 받고 그 경로에 있는 01이라도 추출해야할지도 모른다.

 

 

 

예제 : 영화 제목 하나 가져오기

 

구문

import requests
from bs4 import BeautifulSoup

headers = {'User-Agent' : 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36'}
data = requests.get('https://movie.naver.com/movie/sdb/rank/rmovie.nhn?sel=pnt&date=20200716',headers=headers)

soup = BeautifulSoup(data.text, 'html.parser')

movies = soup.select_one('#old_content>table>tbody>tr:nth-child(2)>td.title>div>a')
print(movies.text)

 

설명

위와 동일한데, movies라는 변수로 가져오는 결과는 다음과 같다. 

이 안에서 그린 북 이라는 글자를 가져오려면, 2가지 방법 title 속성에 있는 그린 북 을 가져오던지, 태그가 감싸고 있는 글자인 그린 북 을 가져올 수 있다. 속성에서 가져오는건 위에서 했으니, 태그가 감싸고 있는 걸 가져오는 방식인 text 함수를 사용해서 가져온다.

 

결과

 

 

예제 : 영화 제목을 싹 다 가져오기

import requests
from bs4 import BeautifulSoup

# URL을 읽어서 HTML를 받아오고,
headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36'}
data = requests.get('https://movie.naver.com/movie/sdb/rank/rmovie.nhn?sel=pnt&date=20200716', headers=headers)

# HTML을 BeautifulSoup이라는 라이브러리를 활용해 검색하기 용이한 상태로 만듦
soup = BeautifulSoup(data.text, 'html.parser')

# select를 이용해서, tr들을 불러오기
movies = soup.select('#old_content > table > tbody > tr')

# movies (tr들) 의 반복문을 돌리기
for movie in movies:
    # movie 안에 a 가 있으면,
    a_tag = movie.select_one('td.title > div > a')
    if a_tag is not None:
        # a의 text를 찍어본다.
        print(a_tag.text)

 

설명

저 그린북부터 맨 아래까지 순위 리스트를 다 가져오려면 셀렉터를 어떻게 지정해야 할까? 생각해보자. 이게 해당 사이트 html을 분석하는 것이다. 분석을 해서 패턴을 찾고, 패턴에 맞게 코딩을 짤 수 있는 것이다.

여기서는 보면, 1, 2, 3 번의 리스트들이 오른쪽에 1, 2, 3 의 <tr>..</tr> 과 매치된다. 이러한 <tr>..</tr>은 바로 상단인 <tbody> 아래에 있다. 그럼 우리는 <tr>..</tr>들만 다 추출을 하면 되지 않을까? 

(실제로 그린북 이라는 글자를 가진 태그는 <a>태그인데, 그 글자를 가진 가장 높은 수준의 태그가 <tr>..</tr> 이라는 것이다. 만약 <tr>..</tr> 보다 더 상위를 가져오면, 그린북 글자가 있는 <a>태그 뿐만 아니라 가버나움, 베일리 어게인 등등 싹다 가져오게 되기 때문에 그 상위를 하면 안되고, 딱 <tr>..</tr>을 해야 한다는 것이다)

 

그럼 이제 아래처럼 tbody 아래에 나열된 <tr>...</tr> 들을 하나하나 오른클릭해서 copy -> copy selector 해보면, 맨 첫번째 tr 부터 다음과 같이 나온다. 

#old_content > table > tbody > tr:nth-child(1)
#old_content > table > tbody > tr:nth-child(2)
#old_content > table > tbody > tr:nth-child(3)

tr:nth-child(1) 값에서 1은 해당 열의 첫번째를 말한다. 그림에서 숫자 1,2,3 이 여기 나온 tr:nth-child(1),(2),(3)과 동일하다.  tr을 다 가져올것이니 tr 옆에 붙은 nth-child(1) 이런건 무시한다. 딱 하나만 가져올거면 무시하면 안되겠지만.

 

그럼 다음과 같이 하면, movies 변수에 tr값들을 싹 다 가져오게 된다.

movies = soup.select('#old_content > table > tbody > tr')

이 movies 변수는 리스트이며, 각각에 tr들을 저장한다. 그래서 반복문으로 가져올 수 있게 된다.

 

movies 변수 안에 있는 내용을 보면 다음과 같다.

각 <tr> .. </tr> 하위내용까지 <tr> .. </tr>들을 싹 다 가져온다.

 

우리의 목적은 그린 북, 가버나움 같은 글자를 가져오고 싶은것이다. 이러한 글자를 가진 태그는 <a> 태그이다. 그래서이제 for 문을 돌려서 다 추출해야 되는데, 그 태그의 selector는 다음과 같다.

#old_content > table > tbody > tr:nth-child(2) > td.title > div > a

 

근데 아래처럼 movies 변수는 이미 tr까지의 값을 가지고 있으므로, tr 바로 아랫단인 td.title 부터 for문을 돌리면 된다.

그래서 아래 구문에서  for문으로 a_tag라는 변수를 select_one 함수로 (td.title > div > a) 1개를 추출하도록 구문을 넣는 것이다.

# select를 이용해서, tr들을 불러오기
movies = soup.select('#old_content > table > tbody > tr')

# movies (tr들) 의 반복문을 돌리기
for movie in movies:
    # movie 안에 a 가 있으면,
    a_tag = movie.select_one('td.title > div > a')
    if a_tag is not None:
        # a의 text를 찍어본다.
        print(a_tag.text)

movie는 for문에서 사용할 새 변수이며, <tr>..</tr> 하나하나를 movie 변수에 넣는다.  movies 함수에는 <tr>..</tr>이 엄청 많기 때문이다. 그중에서 <tr>하나를 꺼내서,  select_one함수를 돌려서 'td.title > div > a' 를 찾아 나온 결과를 a_tag에 저장하는 것이다. 결과적으로, a_tag 변수에는 다음과 같은 값이 들어간다.

 

 

movie는 새 변수이며, movies, 즉 tr묶음에서 <a>

 

 

<a href="/movie/bi/mi/basic.nhn?code=171539" title="그린 북">그린 북</a> 

이 값에 text함수를 씌워 print를 하면 "그린 북" 이것만 나온다. 이걸 for로 돌려서 쭉 나오게 하는 것이다.

 

추가로, 여기 if가 있는데, 왜 있냐면, 실제로 <a> 태그를 찾다보면, 이런식으로 나온다. none이 나온다.

이 none 값은, 아래와 같은 가로줄이다;; 이 가로줄도 빼야하므로 if 문을 쓰게 된다.

 

참고로 가로술의 선택자는 다음과 같다.

#old_content > table > tbody > tr:nth-child(12) > td

저 위에 none 값은, 위에 for문을 돌렸을 때 <tr>..</tr>에 <a>태그가 없는 부분이라 none이라고 뜨는 것임. none이라는 값은 원래 있는 글자가 아니라 파이썬 키워드이다. 값이 없을 때? 뜨는 듯하다. 아래 그림처럼, <tr>..</tr>에 <a>태그가 없는 가로줄이다.

 

그래서 if문으로 none이 나오면 이건 출력하지 않도록 하기 위해, 다음과 같이 if문을 넣는다.

    if a_tag is not None:
        # a의 text를 찍어본다.
        print(a_tag.text)

즉, a_tag (<a>태그가 모아져있는) 변수가 None 이 아니면, print를 한다는 것이다. 그리고 text 함수를 적용하여 딱 이름만 나오게 된다. 와 힘들다.

참고로, if title != None: 을 해야하지만 ,None 쓸 때 한정으로 파이썬에서는 title is not None: 을 쓰길 권고함.

 

 

 

 

실습 : 네이버 영화에서 순위, 제목, 별점을 얻어오기

 

구문

import requests
from bs4 import BeautifulSoup

# URL을 읽어서 HTML를 받아오고,
headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36'}
data = requests.get('https://movie.naver.com/movie/sdb/rank/rmovie.nhn?sel=pnt&date=20200716', headers=headers)

# HTML을 BeautifulSoup이라는 라이브러리를 활용해 검색하기 용이한 상태로 만듦
soup = BeautifulSoup(data.text, 'html.parser')

# select를 이용해서, tr들을 불러오기
movies = soup.select('#old_content > table > tbody > tr')

# movies (tr들) 의 반복문을 돌리기
for movie in movies:
    # movie 안에 a 가 있으면,
    a_tag = movie.select_one('td.title > div > a')
    if a_tag is not None:
        rank = movie.select_one('td:nth-child(1) > img')['alt']  # img 태그의 alt 속성값을 가져오기
        title = a_tag.text  # a 태그 사이의 텍스트를 가져오기
        star = movie.select_one('td.point').text  # td 태그 사이의 텍스트를 가져오기
        print(rank, title, star)

 

분석

 

for 문을 돌리기 직전, movies 선언하는곳까지는 이전 예시와 동일하다. 즉, 우리가 찾으려는 순위, 제목 평점 모두 다 가지고 있는 <tr>태그까지 진입한 상태이다.

 

이제 3개의 값을 확인해야 하므로, 다음과 같이 3개의 값을 크롬 - 검사에서 copy selector로 확인할 수 있다.

순위 : #old_content > table > tbody > tr:nth-child(2) > td:nth-child(1) > img

제목 : #old_content > table > tbody > tr:nth-child(2) > td.title > div > a

평점 : #old_content > table > tbody > tr:nth-child(2) > td.point

 

이제 반복문을 살펴보자

for movie in movies:
    # movie 안에 a 가 있으면,
    a_tag = movie.select_one('td.title > div > a')
    if a_tag is not None:
        rank = movie.select_one('td:nth-child(1) > img')['alt']  # img 태그의 alt 속성값을 가져오기
        title = a_tag.text  # a 태그 사이의 텍스트를 가져오기
        star = movie.select_one('td.point').text  # td 태그 사이의 텍스트를 가져오기
        print(rank, title, star)

<tr> 부터 시작하여, 일단 a를 먼저 찾아서 a_tag에 넣는다.

그리고 a_tag가 not none이라면, 즉 html값이 있는 경우 아래를 수행한다.

rank 변수에 순위인 img값에서 alt의 숫자값을 넣고, title변수에는 a_tag를 text로 가져오고(제목),  star는 td.point 그대로 참고해서 텍스트를 가져오게 한다.

그리고 print로 3개의 변수를 가져온다. 

 

결과

 

 

 

 

 

 

 

 

스크래핑 팁

화면을 분ㅂ석해야함. 어떤 식으로 계층이 되어있는지.

원하는 값의 부모들을  쭉쭉 올라가서 반복문으로 할 수 있는지

중간중간에 값들이 잘 나오는지 체크

사이트마다 코딩하는 내용이 다르니, 해당 사이트의 스크래핑 규칙을 찾아야 됨. 이게 오래걸리고 힘듬.

이 규칙을 찾게되면, 원하는 데이터를 찾을 수 있고 그걸 파이썬으로 만들면 되는거.

내가 원하는 데이터가 어딨는지 계속 개발자도구를 보면서 해야 함.

 

 

 

 

 

연습문제1 : 위 실습에서 날짜만 다른 날의 순위를 동일하게 구해보자.

 

아래처럼 2020년 7월 16일 순위에서, 2018년 3월 27일 순위를 구해보자.

사이트 내부의 html 규칙을 찾는 것 뿐만 아니라, 각 사이트 주소 를 마치 api라 고생각해서, 이런식으로 날짜만 바꿔서 할 수 있다. 즉 아래처럼 맨 뒤 date값만 바꿔준다.

https://movie.naver.com/movie/sdb/rank/rmovie.nhn?sel=pnt&date=20180327

여기서 20180327을 다른숫자로 바꾸면 바로 그날짜의 순위를 크롤링 할 수 있다는 거. 그럼 날짜를 한번에 여러개 할 수도 있겠지.

예를들어 웹페이지에서 날짜를 받고, 그 날짜로 크롤링하는.. 그런

 

 

 

 

연습문제2 : 네이버 한국프로야구 순위 가져오기

https://sports.news.naver.com/kbaseball/record/index.nhn?category=kbo

 

구문

import requests
from bs4 import BeautifulSoup

headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36'}
data = requests.get('https://sports.news.naver.com/kbaseball/record/index.nhn?category=kbo&year=2020', headers=headers)

soup = BeautifulSoup(data.text, 'html.parser')

parse_text = soup.select('#regularTeamRecordList_table > tr')

for parse_text_data in parse_text:
    ranking = parse_text_data.select_one('th > strong').text
    team_name = parse_text_data.select_one('td > div > span').text
    print(ranking, team_name)

아래처럼 랭킹의 경로를 정확히 인지하고, 팀 이름 경로도 정확하게 인지해둔 상태에서 for문을 작성하면 좋다.

랭킹 경로 = soup.select_one('#regularTeamRecordList_table > tr:nth-child(1) > th > strong').text
팀이름 경로 = soup.select_one('#regularTeamRecordList_table > tr:nth-child(1) > td > div > span').text

 

결과

이런식으로 나와야 한다.

 

 

 

연습문제3 : 연습문제2번의 결과에서 승률이 0.5 이상인 팀만 출력하기

 

코드

import requests
from bs4 import BeautifulSoup

headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36'}
data = requests.get('https://sports.news.naver.com/kbaseball/record/index.nhn?category=kbo&year=2020', headers=headers)

soup = BeautifulSoup(data.text, 'html.parser')

# rankings = soup.select_one('#regularTeamRecordList_table > tr:nth-child(1) > th > strong').text
# team_name = soup.select_one('#regularTeamRecordList_table > tr:nth-child(1) > td > div > span').text
# win_rate = soup.select_one('#regularTeamRecordList_table > tr:nth-child(1) > td:nth-child(7) > strong').text

parse_text = soup.select('#regularTeamRecordList_table > tr')

for parse_text_data in parse_text:
    ranking = parse_text_data.select_one('th > strong').text
    team_name = parse_text_data.select_one('td > div > span').text
    win_rate = parse_text_data.select_one('td:nth-child(7) > strong').text
    if float(win_rate) > 0.5:
        print(ranking, team_name, win_rate)

 

if 문 수행 시, win_rate는 글자형태이기 때문에 0.5랑 비교할 수 없어서 에러가 난다. 따라서 float 함수로 win_rate를 실수형으로 바꿔서 0.5랑 비교할 수 있게 하면 된다.

 

 

결과

 

 

 

 

 

 

 

 

 

 

 

 

 

 

몽고디비 실행하기

설치는 위에서 한 대로 하면 되고, 윈도우버전은 관리자권한으로 cmd에서 mongod를 실행한다.

실행 후 브라우저에서 localhost:27017 로 접속했을 때 아래와 같이 나온다면 완료

몽고디비의 디폴트 포트는 27017 이다.

 

 

 

 

 

 

robo3t 실행하기

robo3t는 db에 직접 콘솔로 명령을 보는게 아닌, gui로 쉽게 보기 위한 프로그램이다. 

공식적으로 몽고디비는 compass를 제공한다. 근데 robo3t가 많이 쓰이는 듯 하다.

https://docs.ncloud.com/ko/database/database-10-5.html 혹시 robo3t 말고 compass 쓰고싶으면 여기참고

 

실행하면, 뭐 동의하라고 하고 개인정보 넣는데 그대로 진행하면 됨.

 

아래처럼 창 뜨면 붉은색 표시 create 선택

address를 로컬호스트로 하는 이유는 내 노트북에 몽고디비가 설치되어 있기 때문.

몽고디비와 개발하는 머신이 같은 경우 이렇게 localhost를 쓴다. name과 포트도 잘 써준다.

 

connect를 누르면 접속이 된다.

이렇게 뜨면 접속 완료

 

 

 

 

# 참고 : 데이터베이스의 종류

https://siyoon210.tistory.com/130

 

관계형 데이터베이스

관계형 데이터베이스는 정해진 규칙대로 넣어야만 제대로 데이터가 들어가고, 규칙을 지키지 않으면 데이터를 넣을 수 없음. 그래서 일관성과 무결성이 매우증요하고,  강함.

행/열의 생김새가 정해져있다. 정해진 규칙대로, 즉 정형화되어있다. 예를들어, 이름을 넣으려면 이름 행이 있어야 한다. 아니면 넣을 수 없음.

데이터 50만 개가 적재된 상태에서, 갑자기 중간에 열을 하나 더하기는 상대적으로 어려울 것입니다.

형식은 (데이터베이스 - 테이블 - 데이터들)

ex) postgreSQL(포스트그래 에스큐엘(시퀼)), MySQL(마이 에스큐엘(시퀼)) 등

 

NoSQL

no! 아니고 not only 라는 뜻임.

딕셔너리 형태로 데이터를 저장한다. 같은 값을 가진 데이터도 자유롭게 저장 가능.

장점은 자유롭고, 단점은 잘못 넣으면 무결성이 깨질 수 있음. 컨트롤이 안됨. 근데 무결성을 꼭 할 필요 없을 때 많이 사용.

형식은 (데이터베이스 - 컬렉션 - 데이터들)

 

 

 

 

 

파이썬과 mongodb 연결하기

파이썬에서 pymongo라는 라이브러리가 있어야 아주 쉽게 파이썬에서 몽고디비를 제어할 수 있다.

 

사전작업

pip으로 pymongo 설치

 

구문

from pymongo import MongoClient
client = MongoClient('ukdisk.asuscomm.com', 32768)
db = client.week3_sparta

 

설명

pymongo 패키지로부터 MongoClient를 임포트한다. 

MongoClient함수는 2개의 인수를 받는다. 첫번째는 mongodb 서버 주소, 두번째는 mongodb의 포트

MongoClient함수의 결과를 client 변수에 넣고, week3_sparta 라는 컬렉션을 생성한다. 만약 이미 있는 db라면 생성하지 않고 그대로 사용한다. 그리고 db라는 변수에 결과를 넣는다.

 

 

mongodb에서 데이터 삽입하기

위에 구문 형식은 그대로 가져가며, db, client 변수를 설정에 따라 변경한다.

db명령(insert, find, delete, update 등)은 변수에 넣지 않고 작동해도 되며, 여러가지 로직을 수행하기 위해 변수에 들어간다.

 

구문

# db 연결 및 생성
from pymongo import MongoClient
client =MongoClient('ukdisk.asuscomm.com', 32768)
db = client.week3_sparta

## 데이터 생성하기
db.wow_named.insert_one({'name':  '제이나 프라우드무어', 'race': 'human', 'level': 96})
db.wow_named.insert_one({'name':  '리치왕', 'race': 'undead', 'level': 270})
db.wow_named.insert_one({'name':  '가로쉬 헬스크림', 'race': 'orc', 'level': 99})
db.wow_named.insert_one({'name':  '므우루', 'race': 'naaru', 'level': 142})
db.wow_named.insert_one({'name':  '요그사론', 'race': 'old-gods', 'level': 299})
db.wow_named.insert_one({'name':  '알렉스트라자', 'race': 'dragon', 'level': 273})


#이런식으로 변수를 사용할수도 있다.
character_wow1 = {'name': '티란데 위스퍼윈드', 'race': 'nightelf', 'level': 100}
db.wow_named.insert(chracter_wow1)

 

설명

db.wow_named.insert_one(내용)

붉은색 : 위에서 정의한 데이터베이스를 참고한다. 즉 week3_sparta라는 데이터베이스를 사용

파란색 : 컬렉션. 데이터베이스의 하위 단위이이다. wow_named라는 컬렉션을 생성한다. 원래 있으면 그대로 사용함.

녹색 : 삽입 명령. 해당 week3_sparta라는 데이터베이스의 wow_named라는 컬렉션에 데이터를 삽입한다.

주황색 : 녹색에 대한 조건 및 내용

 

 

결과

 

# 참고 : _id란?

실제로 유저가 넣은 값이 아니고, 각각 열들을 구분하기 위한 식별자로써 저장된다.

 

 

mongodb에서 데이터 읽기

 

find() 함수는 모든 내용을 찾는다. 찾은 내용을 리스트로 받아야 하므로 list() 함수도 함께 사용한다.

find_one() 함수는 하나만 찾는다. 처음에 찾은 것 하나만 나온다.

find와 find_one 모두 인자로 검색할 조건을 명시한다. 해당 조건에 맞는 값만 가져온다. 값을 명시하지 않으면 조건이 없으므로 그대로 모두 찾는다. 

데이터 읽는것이 가장 공부할 게 많고 까다롭다.

출력 결과에서, 길게 나오는건 리스트화된거고, for등으로 보기좋게 출력된것은 다르게 나온것임.

 

기본 방식

 

구문

from pymongo import MongoClient
client =MongoClient('ukdisk.asuscomm.com', 32768)
db = client.week3_sparta

nameds = list(db.wow_named.find({}))

print(nameds[0])

print('=======================')

for named in nameds:
    print(named)

print('=======================')

for named in nameds:
    print(named['name'])

 

결과

 

조건에 맞춰서 가져오기

 

 

현재 데이터

구문

from pymongo import MongoClient
client =MongoClient('ukdisk.asuscomm.com', 32768)
db = client.week3_sparta

# find는 전체를 찾는다. 거기에 level이 96인 캐릭터를 보여준다.
named = list(db.wow_named.find({'level': 96}))
for p in named:
    print(p)

print('=======================')

# find_one 은 가장 먼저 찾은 하나만 보여준다.
selectOne = db.wow_named.find_one({'level': 299})
print(selectOne)

print('=======================')
# find, find_one은 2개의 중괄호를 인수로 가지며, 첫번째 중괄호는 검색조건, 2번째 중괄호는 제외조건? 을 넣는것으로 보임. 중괄호를 계속 추가하는게 아니다.
# for문으로 돌리면, 변수에 list()함수를 적용하지 않아도 잘 작동한다.
except96 = db.wow_named.find({'level': 96}, {'_id': False, 'level': False}) 
#          db.wow_named.find({}, {'_id': 0}))이런식으로도 사용할 수 있음.
for e in except96:
    print(e)

 

결과

 

 

mongodb에서 데이터 업데이트

update_one : 처음에 만나는거 하나만 바꾸고 끝

update_many : 조건에 맞는 모든걸 다 바꿈, 인수로 2개의 중괄호를 받는데, 첫번째 중괄호는 검색 조건, 두번째 중괄호는 변경할 딕셔너리값이다.

 

업데이트문은 꽤 복잡하다. 보통 복붙으로 많이 사용한다. 

업데이트 할 때 many는 비교적 많이 쓰지 않는다. 한번에 많이 하다가 잘못되면 위험하기 때문ㅇ.

 

 

구문

from pymongo import MongoClient
client =MongoClient('ukdisk.asuscomm.com', 32768)
db = client.week3_sparta

updateLevel = db.wow_named.update_many({'level': 96}, {'$set': {'level': 100}})
checkLevel1 = db.wow_named.find({'level': 100})
for level in checkLevel1:
    print(level)
#레벨 96인 사람들을 모두 100으로 변경하고, 출력    

updateRace = db.wow_named.update_one({'race': 'undead'}, {'$set': {'race': 'scorge'}})
checkRace = db.wow_named.find_one({'race': 'scorge'})
print(checkRace)
## 언데드를 스컬지로 바꾸고 스컬지를 출력

 

 

 

mongodb에서 데이터 삭제하기

실제 개발할 때에는, 데이터를 삭제하면 복구가 어렵기 때문에 '삭제' 기능이라도 실제 DB 에서는 데이터 자체를 삭제하는 것보다 '사용하지 않음' 으로 처리해두는 경우가 많다.

 

delete_one : 하나만 지움

delete_many : 모두 지움

 

 

구문

from pymongo import MongoClient
client =MongoClient('ukdisk.asuscomm.com', 32768)
db = client.week3_sparta

# 레벨 99인 캐릭터 하나만 지움
db.wow_named.delete_one({'level': 99})
# 레벨 100인 캐릭터 모두 다 지움
db.wow_named.delete_many({'level': 100})

levelCheck = db.wow_named.find({})
for e in levelCheck:
    print(e)

 

결과

 

 

# 참고 : 몽고디비 뷰 형식 변경하기

 

트리 뷰

 

테이블 뷰

코드 뷰

'

 

 

 

 

 

 

 

실습1 : 크롤링한 데이터를 몽고디비에 넣기

 

import requests
from bs4 import BeautifulSoup

#DATABASE 사용 설정#####################
from pymongo import MongoClient
client = MongoClient('ukdisk.asuscomm.com', 32768)
db = client.week3_sparta
########################################

headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36'}
data = requests.get('https://movie.naver.com/movie/sdb/rank/rmovie.nhn?sel=pnt&date=20200716', headers=headers)

soup = BeautifulSoup(data.text, 'html.parser')

movies = soup.select('#old_content > table > tbody > tr')

for movie in movies:
    a_tag = movie.select_one('td.title > div > a')
    if a_tag is not None:
        rank = movie.select_one('td:nth-child(1) > img')['alt']
        title = a_tag.text
        star = movie.select_one('td.point').text
        #DATABASE에 삽입 #################################
        doc = {
            'rank': rank,
            'title': title,
            'star': star
        }
        db.movies.insert_one(doc)
        ##################################################

설명

크롤링으로 RANK, TITLE, START 3개의 값을 가져오며 doc 변수에 {'rank': '변수'} 형식으로 데이터베이스에 값을 삽입한다. 

 

 

결과

 

 

실습2 : 넣은 데이터에 데이터베이스 명령 사용해보기

월-E의 평점과 같은 평점의 영화 제목들을 가져오기

 

코드

from pymongo import MongoClient
client = MongoClient('ukdisk.asuscomm.com', 32768)
db = client.week3_sparta

walle = db.movies.find_one({'title': '월-E'})['star']
sameStar = db.movies.find({'star': walle})

for s in sameStar:
    print(s['title'])

결과

 

 

'월-E'의 평점과 같은 평점의 영화 제목들의 평점을 0으로 만들기

 

코드

from pymongo import MongoClient
client = MongoClient('ukdisk.asuscomm.com', 32768)
db = client.week3_sparta

walle = db.movies.find_one({'title': '월-E'})['star']

db.movies.update_many({'star': walle}, {'$set': {'star': 0}})

 

결과

 

git , github 사용하기

 

git이란?

작업 기록을 남기고 이력을 추적해 코드를 손쉽게 관리하도록 도와준다. 코드의 수정전, 수정후를 비교하고 최종적으로 fix하는 식이다. 버전관리도 매우 간편하다. 깃허브 데스크톱이라는 프로그램은, gui로 git을 쓸 수 있게 해주는 프로그램이다.

 

유용성

변경작업 문제가 발생했을 원본과 비교

여러사람/컴퓨터에서 코드 작업을 동시에 변경 내역을 정리하는

버전이 여러개면 쓰다가 다른버전으로 변경하는 등을 자유롭게 할 수 있다.

 

예시 (이력을 추적하고 비교하기)

 

github란?

작업 기록과 파일을 저장하는 git 원격 저장소(클라우드) 지원하는 사이트.

또한 최근 트렌드인 오픈소스 찾기, 프로젝트 issue 관리처럼 다양한 부가기능을 제공한다.

다른 비슷한 곳으로 gitlab, bitbucket 등이 있다.

 

 

용어정리

commit

로컬 저장소에 작업을 하고, (예를들어 파이참에서 수정을 하고, git desktop 들어가서) 변경사항을 반영함. 파이참(외에 뭐든,노트패드도 뭐도 ) 등의 내가 쓰는 프로그램에서 수정을 하면, git desktop에서 감지한다. 그걸 commit 변경사항을 "이게 맛다" 갱신하는거.

 

push

원격 저장소에 작업 내역, commit 클라우드에 업로드하는 .

보통 여러 사용자가 올린 것이라도 작업 내역에 겹치는게 없으면 자동으로 합쳐준다.

여러 사용자가 하나의 파일의 동일한 부분을 고쳤다면, 어떤 부분을 어떻게 반영해야할까요? Git이 충돌(conflict)났다고 알려주고 사용자에게 직접 수정하라고 알려줍니다.

push 했으면, 이제 다른 사람들이 있다. (오픈된경우)

 

pull

원격 저장소 작업 내역을 로컬 저장소로 가져오는

내가 컴에서 개발하고 있는것은 이거랑은 상관이 없고, 다른 사람의 작업내역을 가져오거나 다른 컴퓨터에서 작업한 내용을 가져오는 .

예를 들어, 내가 코드 수정하고 commit push , 다른사람이 그걸 수정하고 commit 하고 push하면, 내가 그걸 pull 해서 이어서 작업하는 .

이런 일련의 기록들이 모두 남는다.

 

 

 

github 데스크탑 사용하기

내 자료들을 원하는 경로에 저장해둔다.

깃허브 데스크탑을 실행하고, 아래 화면에서 add a repository to github desktop to start collaborating을 선택

내가 넣어놓은 자료들이 있는 경로를 지정해준다.

아래처럼 뜨면, create a repository 선택하면 됨. 여러가지 내용이있는데 모르겠다. 그냥 create repository 하자. 그러면 조금 오래 걸린다. 기다리자.

다 되면 이런식으로 화면이 나온다.

이것은 기존에 존재하던 homeworks 폴더를 git tracking 있게 설정되었다고 생각하면 된다. 이제 아래 디렉토리처럼 이런 파일이 생긴다.

실제로 내 파일들을 수정하면, 깃허브 데스크탑에서 변동사항을 볼 수 있다.

또 변동한 것을 원복하면 다시 변동사항이 여기서 사라진다.

그리고 변동사항을 git에서 commit 하면 되고,  코멘트를 달고 commit 하면, 적용되고 파일들이 모두 없어짐. 이러면 로컬 저장소에서는 이제 확정된것임. 수정이.

 

이제 깃허브 사이트로 가보자

여기 update shopping.css 누르면,

요런식으로 나옴

변동된 것을 확인할 수 있다.

 

 

 

슬랙과 github 연동하기

슬랙은 다양한 어플이 있고, 깃허브기능이 있는 어플이 있다. 원리는 슬랙에 있는 github 봇을 사용하는 것임.

참고 : slack 메시지 창에 /github라고 쓰고 엔터치면 많은 github 슬랙 앱의 명령어를 있다. 이상 알람이 오지 않도록 unsubscribe 가능

 

 

슬랙 채널에서,, 그러니까 대화방에서 다음과 같이 입력해보자.

/github subscribe uktaekim/sparta-homeworks (계정명/레포지토리명)

이렇게 . install github app 누른다.

쭉 진행한다

 

모든 레포지토리를 공개하지 말고 원하는 것만 공개한다.

이렇게 뜨면 완료이다.

 

슬랙에서도 이러한 메시지가 나온다.

 

이제 깃허브에서 push 하면 이런식으로 뜬다.

 

 

 

 

숙제 ===

 

지니뮤직에서 인기순위 크롤링하고, 몽고디비에 입력하기

 

## 패키지 및 라이브러리 등록
import requests
from bs4 import BeautifulSoup
from pymongo import MongoClient

## 데이터베이스 연결 정보
DB_HOST = '내사이트:32768'
DB_ID = 'root'
DB_PW = '883030aa!'
client = MongoClient('mongodb://%s:%s@%s' % (DB_ID, DB_PW, DB_HOST))
db = client.sparta


## html 크롤링
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36'}
music_data = requests.get('https://www.genie.co.kr/chart/top200?ditc=D&rtm=N&ymd=20200713', headers=headers)
soupped_data = BeautifulSoup(music_data.text, 'html.parser')
music_lists = soupped_data.select('#body-content > div.newest-list > div > table > tbody > tr')

# 데이터 추출
for music_list in music_lists:
    ranking = music_list.select_one('td.number').text[0:2].strip() ## 텍스트 추출 후 앞 2글자만 따온 후 거기에 strip() 함수를 적용
    title = music_list.select_one('td.info > a.title.ellipsis').text.strip()
    artist = music_list.select_one('td.info > a.artist.ellipsis').text.strip()
    print(ranking, "|", title, "|", artist)

    ## DATABASE에 입력
    db.week3_homework.insert_one({'rank': ranking, 'title': title, 'artist': artist})

 

결과

'SCC 9기' 카테고리의 다른 글

[SCC 9기] 5주차  (0) 2020.08.11
[SCC 9기] 4주차  (0) 2020.08.11
[SPC 9기] 2주차  (0) 2020.07.24
[SPC 9기] 1주차 - 프론트엔드  (0) 2020.07.16
[SCC 9기] 개발자를 위한 팁  (0) 2020.07.16

자바스크립트 이어서

 

 

조건문

 

기본형식과 예시

if (조건) {
	내용
} else {
	내용
	}
}

사실 그냥 if if if if 해서 쓸수 있는데, else를 쓰는 경우는 우리가 생각하지 못한 예외까지 포함하기 때문.

 

예시 : 어떤 값이 90보다 작으면 작다고, 크면 크다고 알려주는 함수

예시 : 딕셔너리 사용하기

 

* 조건이 여러개일 때 사용하는 연산자 : and , or

 

설명

and : 기호는 && 이며, 두개의 조건 모두 true여야만 ture를 낸다.

or : 기호는 || 이며, 두 개의 조건 중 하나만 true여도 true를 낸다. 물론 둘 다 false라면 false를 낸다.

 

예시

나이가 20과 같거나 크고 30보다 작은 경우, 즉 20대인지 여부를 알려주는 함수. 

or (||) 도 아래와 동일한 방식으로 하면 된다.

 

 

* 조건이 여러개 있는 경우 : else if

 

설명

일반 조건문에서 else 대신 else if , else if ... 쭉 가다가 마지막에 else 로 마무리할 수 있다.

그냥 if if if if 를 하면 되는데 왜 else if 를 쓰는가? if는 무조건 실행하게 되어있다. 따라서 if를 여러개 쓰는 경우, 선행 if가 완료되어도 그다음 if를 무조건 실행한다. 하지만 else if는 선행 if가 되었으면 else if는 수행하지 않는다.

 

예시

여기서 만약 else if를 if로 했다면, 120 보다 큰거 검사 해서 와 19세기 태어났군요 나오고 끝나는게 아니고, 이거 나오고 또 80세이상! 이것도 나온다. 즉 모든 if를 다 실행해버림.

 

보통 if if if 이런식으로 잘 안쓸 것 같은데, 의도적으로 쓰는 경우가 있긴 있다. 여러개 검사를 꼭 해야 할때 등.. 암튼 필요할 수 있다. 실제로 업무에서는 복잡한 if문은 잘 안쓰고, 단순한 걸 많이 쓰게 된다고 한다. (else도 없는..)

 

 

 

반복문

만약 0부터 99까지 출력하라고 한다면, console.log(0) 부터 시작해서 99까지 할 순 없다. 이걸 간단하게 하는게 반복문이며, for 문이라고 한다. 대부분의 프로그램들은 반복문이 for 이다. 

 

참고 : 대부분의 프로그래밍 기본 패턴은 조건문+반복문이 조합된다.

 

* 반복문의 형태

 

반복문에는 3가지가 들어가며, 세미콜론으로 구분된다. for문이 시작하면, 먼저 시작조건을 검사하고, 아래 액션을 수행한 후 3번째인 반복후액션을 수행하고 종료조건을 검사하고 조건이 참이면 다시 아래 구문을 실행한다. 이걸 반복한다. 반복하면서 종료조건에서 false가 나오면 안에 구문 실행하지 않고 종료조건에서 바로 종료된다.

for (시작조건; 반복조건; 매 반복후 할일) {
	실행할것;
}

- 시작조건 : 시작할때의 조건

- 반복조건 : 해당 조건이 false가 되면 반복문이 끝난다.

- 매 반복 후 할일 : 반복문 안에 본문 실행 후에 여기 있는걸 하고, 다시 반복조건을 검사한다. 

                        

# 참고 : 카운터변수 i

i++ : i에다가 1을 더해서 다시 i에 넣어라 라는 뜻.

i는 프로그래밍에서 카운터변수(counter variable)라고 하며 보통  for문에서 관습적으로 많이 사용한다.

 

기본예시

let wizards = ['덤블도어','맥고나걸','스네이프','해리','허마이오니','론']

// 리스트이름.length 기억나시죠? 리스트의 길이!
// i가 1씩 증가하면서, 마법사들 wizards의 원소를 차례대로 불러올 수 있게 됩니다.
// 이렇게 하면 리스트의 모든 원소를 한번에 출력할 수 있겠죠?
for (let i = 0 ; i < wizards.length ; i++) {
	console.log(wizards[i])
}

 

* 반복문과 리스트, 딕셔너리

반목문은 리스트와 단짝이다. 함께 자주 쓰인다. 리스트와 반복문이 쓰일때는, 리스트를 처음부터 끝까지 검사하기 위해 "리스트명.length" 형식으로 반복조건을 많이 사용한다. 

 

자주 쓰는 패턴 중 하나는 먼저 "리스트+딕셔너리" 를 선언하고, 반복문에서 출력할 수 있다.

 

예시 : 리스트 내의 딕셔너리를 모두 출력하기

let wizardsInfo = [
	{'name':'덤블도어', 'age':116},
	{'name':'맥고나걸', 'age':85},
	{'name':'스네이프', 'age':60},
  {'name':'해리', 'age':40},
  {'name':'허마이오니', 'age':40},
  {'name':'론', 'age':40},
]

for (let i = 0 ; i < wizardsInfo.length ; i++) {
	console.log(wizardsInfo[i]);
}

 

예시 :아래는 70 미만인 마법사 이름을 출력하는 반복문 (위에 변수를 그대로 사용)

i는 0부터 시작하고, wizardinfo의 길이가 6이므로, i가 6보다 작으면 계속 진행한다. i는 매번 1씩 올린다. 만약 위자드정보에서 age가 70보다 적으면, 예시와 같이 console.log로 이름을 출력하고 아니면 암것도 안하고 이걸 반복한다.

 

 

* 반복문 예시

 

연습1 - 반복문, 합을 구하는 함수 만들기

0부터 n-1까지 더하는 함수를 만들고 싶다면?  (  0부터 n-1 이라는 것은, 0이 들어가기 때문에 1개를 빼는것임..)

function calculateSum(n) {
    let sum = 0
    for (let i = 0; i < n; i++) {
        sum += i;         // sum을 i만큼 증가시켜라. sum = sum + i 와 동일!
    }
    return sum
}

let result = calculateSum(10); // return 결과인 sum이 result에 저장
console.log(result)       // 45를 출력

처음에 sum

 

연습2 - 다음 리스트에서 딸기는 몇 개일까?

이 연습은 프로그래밍의 기본에서 매우 중요함. 반복되는 코드를 이렇게 함수로 만들고, 확장해나가는 것.

let fruitsBasket = ['사과','감','감','배','포도','포도','딸기','포도','감','수박','딸기']

function countFruit(name) {
  let count = 0;
  for (let i = 0; i < fruitsBasket.length; i++) {
    let fruit = fruitsBasket[i];
    if (fruit == name) {
      count += 1;
    }
  }
  return count;
}

// 함수로 만들어두면! 같은 코드을 또 적을 필요가 없죠!
let gamCount = countFruit("감");
console.log(gamCount);

let subakCount = countFruit("수박");
console.log(subakCount);

 

연습3 - 오픈api일부를 가져와서, 미세먼지(pm10)의 값이 25미만인 측정소 (msrste_nm)과 미세먼지 수치 출력하기

이 패턴도 익숙해져야 한다. open api는 아래서 자세히 배울 것이다. 해당 값은 서울시에서 제공하는 특정 시점의 미세먼지 수치 중 일부이다. 이 값은 리스트+딕셔너리 형식이다. 이 값을 가지고 아래 문제를 풀어보자.

let cityAir = [
  {
    MSRDT: "201912052100",
    MSRRGN_NM: "도심권",
    MSRSTE_NM: "중구",
    PM10: 22,
    PM25: 14,
    O3: 0.018,
    NO2: 0.015,
    CO: 0.4,
    SO2: 0.002,
    IDEX_NM: "좋음",
    IDEX_MVL: 31,
    ARPLT_MAIN: "O3",
  },
  {
    MSRDT: "201912052100",
    MSRRGN_NM: "도심권",
    MSRSTE_NM: "종로구",
    PM10: 24,
    PM25: 18,
    O3: 0.013,
    NO2: 0.016,
    CO: 0.4,
    SO2: 0.003,
    IDEX_NM: "좋음",
    IDEX_MVL: 39,
    ARPLT_MAIN: "PM25",
  },
  {
    MSRDT: "201912052100",
    MSRRGN_NM: "도심권",
    MSRSTE_NM: "용산구",
    PM10: 0,
    PM25: 15,
    O3: 0.012,
    NO2: 0.027,
    CO: 0.4,
    SO2: 0.003,
    IDEX_NM: "점검중",
    IDEX_MVL: -99,
    ARPLT_MAIN: "점검중",
  },
  {
    MSRDT: "201912052100",
    MSRRGN_NM: "서북권",
    MSRSTE_NM: "은평구",
    PM10: 36,
    PM25: 14,
    O3: 0.019,
    NO2: 0.018,
    CO: 0.5,
    SO2: 0.005,
    IDEX_NM: "좋음",
    IDEX_MVL: 42,
    ARPLT_MAIN: "PM10",
  },
  {
    MSRDT: "201912052100",
    MSRRGN_NM: "서북권",
    MSRSTE_NM: "서대문구",
    PM10: 28,
    PM25: 9,
    O3: 0.018,
    NO2: 0.015,
    CO: 0.6,
    SO2: 0.004,
    IDEX_NM: "좋음",
    IDEX_MVL: 37,
    ARPLT_MAIN: "PM10",
  },
  {
    MSRDT: "201912052100",
    MSRRGN_NM: "서북권",
    MSRSTE_NM: "마포구",
    PM10: 26,
    PM25: 8,
    O3: 0.012,
    NO2: 0.021,
    CO: 0.5,
    SO2: 0.003,
    IDEX_NM: "좋음",
    IDEX_MVL: 36,
    ARPLT_MAIN: "NO2",
  },
  {
    MSRDT: "201912052100",
    MSRRGN_NM: "동북권",
    MSRSTE_NM: "광진구",
    PM10: 17,
    PM25: 9,
    O3: 0.018,
    NO2: 0.016,
    CO: 0.6,
    SO2: 0.001,
    IDEX_NM: "좋음",
    IDEX_MVL: 31,
    ARPLT_MAIN: "O3",
  },
  {
    MSRDT: "201912052100",
    MSRRGN_NM: "동북권",
    MSRSTE_NM: "성동구",
    PM10: 21,
    PM25: 12,
    O3: 0.018,
    NO2: 0.017,
    CO: 0.4,
    SO2: 0.003,
    IDEX_NM: "좋음",
    IDEX_MVL: 33,
    ARPLT_MAIN: "PM25",
  },
  {
    MSRDT: "201912052100",
    MSRRGN_NM: "동북권",
    MSRSTE_NM: "중랑구",
    PM10: 27,
    PM25: 10,
    O3: 0.015,
    NO2: 0.019,
    CO: 0.4,
    SO2: 0.003,
    IDEX_NM: "좋음",
    IDEX_MVL: 34,
    ARPLT_MAIN: "PM10",
  },
  {
    MSRDT: "201912052100",
    MSRRGN_NM: "동북권",
    MSRSTE_NM: "동대문구",
    PM10: 26,
    PM25: 9,
    O3: 0.016,
    NO2: 0.017,
    CO: 0.4,
    SO2: 0.003,
    IDEX_NM: "좋음",
    IDEX_MVL: 34,
    ARPLT_MAIN: "PM10",
  },
  {
    MSRDT: "201912052100",
    MSRRGN_NM: "동북권",
    MSRSTE_NM: "성북구",
    PM10: 27,
    PM25: 8,
    O3: 0.022,
    NO2: 0.014,
    CO: 0.5,
    SO2: 0.003,
    IDEX_NM: "좋음",
    IDEX_MVL: 37,
    ARPLT_MAIN: "PM10",
  },
  {
    MSRDT: "201912052100",
    MSRRGN_NM: "동북권",
    MSRSTE_NM: "도봉구",
    PM10: 25,
    PM25: 12,
    O3: 0.024,
    NO2: 0.011,
    CO: 0.3,
    SO2: 0.002,
    IDEX_NM: "좋음",
    IDEX_MVL: 41,
    ARPLT_MAIN: "O3",
  },
  {
    MSRDT: "201912052100",
    MSRRGN_NM: "동북권",
    MSRSTE_NM: "강북구",
    PM10: 30,
    PM25: 15,
    O3: 0.022,
    NO2: 0.02,
    CO: 0.4,
    SO2: 0.002,
    IDEX_NM: "좋음",
    IDEX_MVL: 39,
    ARPLT_MAIN: "PM10",
  },
 ];

아래 두 질문에 대답하기 전에, 먼저 위 스크립트 내용을 그대로 복붙하여 cityAir라는 변수를 선언하고 값들을 넣는다.

 

 

Q1. "PM10" 수치가 25㎍/㎥보다 작은 측정소(MSRSTE_NM) 를 모두 출력해보세요.

for (let i = 0; i < cityAir.length; i++) {
  let mise = cityAir[i];
  if (mise["PM10"] < 25) {
    let guName = mise["MSRSTE_NM"];
    let guMise = mise["PM10"];
    console.log("25㎍/㎥보다 작은 구: " + guName + ", " + guMise);
  }
}

 

Q2. 함수 만들기 - 입력받은 미세먼지 수치보다 작은 측정소(MSRSTE_NM) 를 출력하는 함수를 만들어보세요.

function showUnderMise(index) {
  for (let i = 0; i < cityAir.length; i++) {
    let mise = cityAir[i];
    if (mise["PM10"] < index) {
      let guName = mise["MSRSTE_NM"];
      let guMise = mise["PM10"];
	    console.log(index + "보다 작은 구: " + guName + ", " + guMise);
    }
  }
}

 

 

 

 

 

 jquey란?

https://jquery.com/

 

jQuery

What is jQuery? jQuery is a fast, small, and feature-rich JavaScript library. It makes things like HTML document traversal and manipulation, event handling, animation, and Ajax much simpler with an easy-to-use API that works across a multitude of browsers.

jquery.com

자바스크립트를 일일이 다 치는 것은 비효율적이다. 여느 프로그래밍 언어와 마찬가지로 있는걸 가져다 쓰는 능력이 중요하다.  jquery는 html의 요소들을 조작하고 화면의 움직임을 처리하는 등 편리하고 자주 쓰이는 자바스크립트 코드를 미리 작성해놓은 묶음이다. 즉 자바스크립트의 라이브러리 중 하나. 또한 함수들의 명시적이라 알아보기도 편하다.

 

자바스크립트로도 뭐든 기능을 구현할 수 있지만(버튼 글씨 바꾸기, 숨기기 등), jquery를 쓰면 복잡한코드르 줄일 수 있고, 브라우저간 호환성도 고려해야 해서, jquery라는 라이브러리가 등장했다.(브라우저 호환성은 좀 옛날얘기)

 

 

# 참고 : 라이브러리란?

공통으로 사용할 수 있는 특정 기능이나 함수들의 모음이다. 1주차에서 javascript 배울 때, 소문자를 대문자로 바꾸는 함수, 문자를 나누는 함수 등을 썼는데, 이건 자바스크립트에서 기본으로 제공하는 라이브러리를 사용한 것이다. 즉 추가 라이브러리를 넣을 수 있고, 라이브러리를 사용하면 내가 모든 걸 다 만들지 않아도 라이브러리에 있는 함수를 가져다 쓸 수 있다.

 

# 참고 : 동일한 기능을 자바스크립트로 썼을 때와 jquery를 썼을 때 비교

자바스크립트 :  document.getElementById("element").style.display = "none";

jquery :  $('#element').hide();

 

 

jquery 시작하기

미리 작성된 자바스크립트 코드를 가져오는 것을 import라고 하는데, jquery도 부트스트랩처럼 임포트를 해야 함. jquery는 자바스크립트와 다른 특별한 게 아니고, 미리 작성된 javascript 코드들의 모음이다. 전문 개발자들이 짜 놓은 것. 이걸 임포트하는 것이다.

 

* import 방법

<head></head>사이에 아래 한 줄을 넣으면 완료.

<script src="<https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js>"></script>

여기 보면 버전이 3.5.1 인 jquery를 확인할 수 있다. 이 jquery의 버전과 부트스트랩의 버전도 호환되는지 확인해야 할 필요가 있을 수 있다. 현재 3.5.1은 부트스트랩과 호환된다고 한다.

 

# 참고 : 더 자세히 살펴보기

jquery 시작하기 : https://www.w3schools.com/jquery/jquery_get_started.asp

jquery 관련 정보 : https://code.jquery.com/ (공식CDN)

 

 

 

 

 

JQUERY 사용하기

jquery도 특정 태그에 어떤 액션을 해라 라고 할 수 있어서, jquery도 css처럼 선택자를 쓸 수 있고, 선택자로 id라는 걸 씀. (css에서는 선택자로 class를 썼다. (마침표를 앞에 붙이는 방식)

jquery는 id라는 선택자 값을 통해 특정버튼,인풋박스,div등을 가리키는 게 일반적이다.

 

예를들어, 특정 인풋박스의 값을 -> 가져와라 / 특정 DIV를 -> 안보이게 해라.   이런식이다.

 

특정 태그에 jquery로 접근하려면 id를 정의해야 함. post-url은 id로 input 태그에 들어가있다. id로 정의한 곳을 선택하고 함수를 적용하거나 html에서 사용할 수 있다. 

$('#id로 정의한 이름')

 

 

jquery 패턴 연습하기

절대 모든 jquery를 외울필요 없다.  스크롤을 감지하는 함수, 화면 사이즈를 변경할 때 반응하는 함수, 여러가지 엄청 많다. 이런것도 필요에 따라 구글링을 해야 한다.

 

1. 사용자가 입력한 값을 가져오기

 

이 내용은 아래 태그인데, 여기서 post-url 을 주목하자.

val() 함수(입력된 값을 가져옴)를 사용하여 아래와 같이 사용할 수 있다.

$('#post-url').val();

그러면 efsdfd를 출력하게 된다. 다시 정리하면, #post-url은 html 내에 post-url이라는 id를 찾으라는 의미이며, 그 값을 가져와서 jquery에 정의된 함수 중 하나인 val()을 사용하라는 것이다.

 

아래처럼 값을 가져온다. 즉 유저가 입력한 값을 받아올 수 있음.

아래처럼 변수에다 이 결과를 넣을수도 있다.

 

추가로, 아래처럼 직접 텍스트를 입력하면 직접 입력하지 않고 입력할수도 있다.

$('#post-url').val('여기에 텍스트를 입력하면!');

즉, val 함수는 해당 텍스트박스에 글을 넣느냐 받느냐 등을 할 수 있다.

 

 

 

2. 개체를 없앴다가 나오게 하기

 

개체를 사라지게 하는 함수는 hide() 이고, 나오게 하는 함수는 show() 이다.

post-box라는 id를 가진 개체에 대해, 다음과 같이 적용할 수 있다.

$('#post-box').hide();
$('#post-box').show();

보면 맨 윗단 div에 post-box가 적용되어 있으므로, 이 div에 대해 hide를 하기도 하고 show를 할수도 있는 것이다.

 

 

3. CSS 속성값을 가져오고 수정해보기

실제로 hide() 함수로 숨겼지만, 이 hide() 함수는 CSS의 어떤 값을 변경한 것 뿐이다. 어떤 작업을 했는지 확인해보자.

css() 함수를 사용한다. css(속성) 으로 쓸 수 있다.

위와 같이, hide를 한 후에 해당 post-box의 css속성 중 display를 보니 none 라는 결과가 나왔다.  이제 show를 한 후, 다시 속성을 확인해보니 block이라고 나왔다.

 

즉 hide 함수는 css 속성 중 display속성에 대해 none 값을 줘서 숨기는 것이고, show는 css 속성 중 display 속성에 대해 block 값을 줘서 나오게 하는 것이라고 이해할 수 있다. 이걸 jquery를 안쓰고 할려면 복잡한데, jquery를 쓰니 쉽게 할 수 있는 것이다.

 

이렇게 속성을 확인해야 하는 이유는, 조건문이나 반복문 등을 사용할 때, 이런 것(none, block)을 조건으로 할 수 있기 때문이다.

 

속성을 가져올 수 있다면, 속성을 변경할수도 있다. 아래 예시는 width 값을 바꾸는 예시이다.

현재 post-box의 width를 확인한 후, 300px로 변경했다.

 

원래 이랬는데,

요롷게 바뀌었다.

그렇다면, jquery를 쓰지 않고도, 속성을 변경해서 개체를 숨기거나 보이게 할 수 있다.

참고 : 여기서 개체는 안보이거나 보이는거지 없애거나 만드는 게 아니다.

 

 

4. 글자를 바꾸기

text() 함수를 사용하여 개체의 글자를 바꿀 수 있다. 원하는 대상이 되는 개체(아래 예시에서는 버튼)에 id값을 주고, text() 함수를 적용하면 글자를 바꿀 수 있다. 아래 코드는 btn-post-box라는 id를 가지고 있다.

<button onclick="openClose()" id="btn-post-box" type="button" class="btn btn-primary">포스팅 박스 열기 </button>

$('#btn-post-box').text('포스팅 박스 닫기'); 를 입력하면, 아래와 같이 변경된다.

 

 

 

5. html 코드 자체를 삽입하기

append() 함수를 사용하여, html 코드 자체를 삽입할 수 있다.

다음과 같이 변수를 생성한다.

let tempHtml = '<button>나는 추가될 버튼이다!</button>';

아래처럼, card-box라는 곳에 위 html 코드를 붙여넣는다. (이 상태는 html이 아니고 문자열 상태임. 이 문자열이, html에 들어가서 html이 되는 것)

$('#cards-box').append(tempHtml);

아래처럼 card-box를 찾을 수 있다.

이런식으로 붙는걸 볼 수 있다. 여러번하면, 여러개 생긴다.

 

이제 이런 작은 내용 말고, 하나의 카드를 넣어보자.

 

저기 tempHtml 옆에 <div> 로 시작하는 큰 html 값이 변수에 들어가서 해당 값을 붙여넣을 수 있다. 이 변수에는 엄청 큰 html값도 들어갈 수 있다고 한다.

 

카드가 붙는데, 붙는 순서가 좀 이상하다..;; 이건 이렇게부트스트랩에서 정해놓은거라 순서를 직접 조절하긴 조금 어렵다.

 

# 중요! : 백틱

백틱 ( ` ) 은 html코드에다가 자바스크립트 변수를 사용하고 싶을 떄, 홀따옴표가 아닌 백틱으로 감싸야 한다. 이것은 약속이므로 꼭 지켜야 한다.

 

여긴 백틱이 안들어간다. 내용에 자바스크립트 변수가 없으니까.

 

여긴 백틱이 들어간다. 자바스크립트 변수가 있으니까.

또한, 백틱 안에 html을 넣으면, 줄바꿈상관없이 한 문장으로 인식한다. 

`<divclass="card">
	<imgclass="card-img-top"
	src="https://www.eurail.com/content/dam/images/eurail/Italy%20OCP%20Promo%20Block.adaptive.767.1535627244182.jpg"
	alt="Cardimagecap">
	<divclass="card-body">
	<ahref="#"class="card-title">여기기사제목이들어가죠</a>
	<pclass="card-text">기사의요약내용이들어갑니다.동해물과백두산이마르고닳도록하느님이보우하사우리나라만세무궁화삼천리화려강산...</p>
	<pclass="card-textcomment">여기에코멘트가들어갑니다.</p>
	</div>
</div>`

 

 

 

 

실습 : 나홀로 메모장에 여러가지 설정해보기

 

완성된 모습 : 열기 버튼을 누르면 아래 붉은 네모칸 내용이 생기게 해야 함.

 

여기서 핵심은 "포스팅 박스 열기" 버튼에 자바스크립트 넣는 것이다.

 

해당 버튼을 누르면 : 

- 버튼의 글자가 변경 (열기 <-> 닫기)

- 포스팅 박스 상태 변경 (나오기 <-> 숨기기)

두 가지를 해야 한다.

 

 

1. 버튼에 onclick 속성과 함수 적용

- 아래와 같이 onclick 속성 (클릭하면 액션을 취함) 을 주고, openClose()라는 함수를 설정한다.

<button onclick="openClose()" id="btn-post-box" type="button" class="btn btn-primary">포스팅 박스 열기</button>

 

2. 없어졌다 보였다 할 개체인 post-box에 id 적용 

아래 html이 위에 붉은네모칸 표시한 포스트박스이다. id를 post-box라고 정의했다.

 

3. 버튼을 눌렀을 때 발동할 함수를 생성

처음부터 post-box를 감춰두고, 누르면 열리고 누르면 닫히게 하고, 글씨도 닫기/열기 변경하도록.

 

4. 최종 결과물

<!Doctype html>
<html lang="ko">
<head>
        <!-- Required meta tags -->
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<!-- Bootstrap CSS -->
        <link rel="stylesheet" href="<https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css>"
              integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm"
              crossorigin="anonymous">
<!-- JS -->
        <script src="<https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js>"></script>
        <script src="<https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js>"
                integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q"
                crossorigin="anonymous"></script>
<!-- 구글폰트 -->
        <link href="<https://fonts.googleapis.com/css?family=Stylish&display=swap>" rel="stylesheet">
<title>스파르타코딩클럽 | 나홀로 메모장</title>
<!-- style -->
        <style type="text/css">
            * {
                font-family: 'Stylish', sans-serif;
            }
.wrap {
                width: 900px;
                margin: auto;
            }
.comment {
                color: blue;
                font-weight: bold;
            }
#post-box {
                width: 500px;
                margin: 20px auto;
                padding: 50px;
                border: black solid;
                border-radius: 5px;
            }
        </style>
        <script>
            function openClose() {
                // id 값 post-box의 display 값이 block 이면(= 눈에 보이면)
                if ($('#post-box').css('display') == 'block') {
                    // post-box를 가리고
                    $('#post-box').hide();
                    // 다시 버튼을 클릭하면, 박스 열기를 할 수 있게 텍스트 바꿔두기
                    $('#btn-post-box').text('포스팅 박스 열기');
                } else {
                    // 아니면(눈에 보이지 않으면) post-box를 펴라
                    $('#post-box').show();
                    // 다시 버튼을 클릭하면, 박스 닫기를 할 수 있게 텍스트 바꿔두기
                    $('#btn-post-box').text('포스팅 박스 닫기');
                }
            }
        </script>
</head>
<body>
        <div class="wrap">
            <div class="jumbotron">
                <h1 class="display-4">나홀로 링크 메모장!</h1>
                <p class="lead">중요한 링크를 저장해두고, 나중에 볼 수 있는 공간입니다</p>
                <hr class="my-4">
                <p class="lead">
                    <button onclick="openClose()" id="btn-post-box" type="button" class="btn btn-primary">포스팅 박스 열기
                    </button>
                </p>
            </div>
            <div id="post-box" class="form-post" style="display:none">  이부분을 위에 <style> 태그 안에 있는 $post-box 에다가 display: none 해도 됨
                <div>
                    <div class="form-group">
                        <label for="post-url">아티클 URL</label>
                        <input id="post-url" class="form-control" placeholder="">
                    </div>
                    <div class="form-group">
                        <label for="post-comment">간단 코멘트</label>
                        <textarea id="post-comment" class="form-control" rows="2"></textarea>
                    </div>
                    <button type="button" class="btn btn-primary">기사저장</button>
                </div>
            </div>
            <div id="cards-box" class="card-columns">
                <div class="card">
                    <img class="card-img-top"
                         src="<https://www.eurail.com/content/dam/images/eurail/Italy%20OCP%20Promo%20Block.adaptive.767.1535627244182.jpg>"
                         alt="Card image cap">
                    <div class="card-body">
                        <a href="#" class="card-title">여기 기사 제목이 들어가죠</a>
                        <p class="card-text">기사의 요약 내용이 들어갑니다. 동해물과 백두산이 마르고 닳도록 하느님이 보우하사 우리나라만세 무궁화 삼천리 화려강산...</p>
                        <p class="card-text comment">여기에 코멘트가 들어갑니다.</p>
                    </div>
                </div>
                <div class="card">
                    <img class="card-img-top"
                         src="<https://www.eurail.com/content/dam/images/eurail/Italy%20OCP%20Promo%20Block.adaptive.767.1535627244182.jpg>"
                         alt="Card image cap">
                    <div class="card-body">
                        <a href="#" class="card-title">여기 기사 제목이 들어가죠</a>
                        <p class="card-text">기사의 요약 내용이 들어갑니다. 동해물과 백두산이 마르고 닳도록 하느님이 보우하사 우리나라만세 무궁화 삼천리 화려강산...</p>
                        <p class="card-text comment">여기에 코멘트가 들어갑니다.</p>
                    </div>
                </div>
                <div class="card">
                    <img class="card-img-top"
                         src="<https://www.eurail.com/content/dam/images/eurail/Italy%20OCP%20Promo%20Block.adaptive.767.1535627244182.jpg>"
                         alt="Card image cap">
                    <div class="card-body">
                        <a href="#" class="card-title">여기 기사 제목이 들어가죠</a>
                        <p class="card-text">기사의 요약 내용이 들어갑니다. 동해물과 백두산이 마르고 닳도록 하느님이 보우하사 우리나라만세 무궁화 삼천리 화려강산...</p>
                        <p class="card-text comment">여기에 코멘트가 들어갑니다.</p>
                    </div>
                </div>
                <div class="card">
                    <img class="card-img-top"
                         src="<https://www.eurail.com/content/dam/images/eurail/Italy%20OCP%20Promo%20Block.adaptive.767.1535627244182.jpg>"
                         alt="Card image cap">
                    <div class="card-body">
                        <a href="#" class="card-title">여기 기사 제목이 들어가죠</a>
                        <p class="card-text">기사의 요약 내용이 들어갑니다. 동해물과 백두산이 마르고 닳도록 하느님이 보우하사 우리나라만세 무궁화 삼천리 화려강산...</p>
                        <p class="card-text comment">여기에 코멘트가 들어갑니다.</p>
                    </div>
                </div>
                <div class="card">
                    <img class="card-img-top"
                         src="<https://www.eurail.com/content/dam/images/eurail/Italy%20OCP%20Promo%20Block.adaptive.767.1535627244182.jpg>"
                         alt="Card image cap">
                    <div class="card-body">
                        <a href="#" class="card-title">여기 기사 제목이 들어가죠</a>
                        <p class="card-text">기사의 요약 내용이 들어갑니다. 동해물과 백두산이 마르고 닳도록 하느님이 보우하사 우리나라만세 무궁화 삼천리 화려강산...</p>
                        <p class="card-text comment">여기에 코멘트가 들어갑니다.</p>
                    </div>
                </div>
                <div class="card">
                    <img class="card-img-top"
                         src="<https://www.eurail.com/content/dam/images/eurail/Italy%20OCP%20Promo%20Block.adaptive.767.1535627244182.jpg>"
                         alt="Card image cap">
                    <div class="card-body">
                        <a href="#" class="card-title">여기 기사 제목이 들어가죠</a>
                        <p class="card-text">기사의 요약 내용이 들어갑니다. 동해물과 백두산이 마르고 닳도록 하느님이 보우하사 우리나라만세 무궁화 삼천리 화려강산...</p>
                        <p class="card-text comment">여기에 코멘트가 들어갑니다.</p>
                    </div>
                </div>
            </div>
        </div>
    </body>
</html>

 

 

 

 

 

추가실습

 

실습1 : jqury + javascript 조합

 

다음과 같은 사이트를 만들고,

이름을 쓰고, 이름붙이기 버튼을 누르면 아래에 계속 누적되고, 다지우기 버튼을 누르면 다 없어지는 식.

그래서, "이름붙이기" 에 함수 , "다지우기" 에 함수 2개의 함수를 만들고, 각 함수를 버튼에 적용하면 된다.

# 참고 : 자바스크립트 내에서 변수를 쓸 때는 그냥 변수명만 명시하면 되는데, html 안에서 자바스크립트 변수를 쓸 때는 백틱`` 안에 넣어야 하고 또 변수 이름을 ${} 안에 넣어야 한다.

 

리스트에다가 jquery 값을 넣어야 함.

 

1. 인풋내용을 받아서

2. 그 내용을 html로 덧붙일 수 있어야 하고

3. 버튼을 누르면 2번이 작동해야 하고

4. 다지우기 누르면 싹 다 날림.

 

 

정답

<!doctype html>
<html lang="ko">

<head>
    <meta charset="UTF-8">
    <title>jQuery 연습하고 가기!</title>

    <!-- JQuery를 import 합니다 -->
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>

    <style type="text/css">
        div.question-box {
            margin: 10px 0 20px 0;
        }
    </style>

    <script>
        function showName() {
            let inputName = $('#input-name').val();
            if (inputName == '') {
                alert("아무것도 안썼쟈너..ㅜㅜ");
                retrun; // 써도 되고 안써도 됐다. 뭐가 다를까?
            }
            let tagName = `<li>${inputName}</li>`;  실제로 붙는 쪽에 html을 보자. 딱 <li></li> 이다.
                $('#names').append(tagName);
            }
        function removeNames() {
            $('#names').empty()
            // 1. names의 내부 태그를 모두 비운다.(jQuery의 $('....').empty()를 이용하면 굿!)
        }
    </script>

</head>

<body>
    <h1>jQuery + Javascript의 조합을 연습하자!</h1>

    <div class="question-box">
        <h2>HTML 붙이기/지우기 연습</h2>
        <h5>1. 이름을 입력하면 아래 나오게 하기</h5>
        <h5>2. 다지우기 버튼을 만들기</h5>
        <input id="input-name" type="text" placeholder="여기에 이름을 입력" />
        <button onclick="showName()">이름 붙이기</button>
        <button onclick="removeNames()">다지우기</button>
        <ul id="names">
            <li>김스파</li>
            <li>박르탄</li>
        </ul>
    </div>
</body>

</html>

 

 

실습2 : 조건문 - 1,2,3,4 .. 높여가며 누른 횟수를 보여주는 onclick 함수 만들어보기

<!doctype html>
<html lang="ko">

<head>
    <meta charset="UTF-8">
    <title>Javascript를 연습하자!</title>

    <style type="text/css">
        div.question-box {
            margin: 10px 0 20px 0;
        }
    </style>

    <script>
        // 버튼을 누른 횟수를 저장할 변수 선언
		let count = 0;
		function showButtonCount() {
            count += 1;
            if (count < 20) {
                alert(`안녕! ${count}번 누르셨네요!`);
            } else {
                alert('앗.. 더이상 누르면 안돼버렷!');
            }
        }
		    // 1. 버튼을 누를 때마다(함수를 호출할 때마다) 누른 횟수 증가
			// 2. 누른 횟수가 20 미만이면 "안녕! {{실제 누른 횟수}} 번 누르셨네요!" alert 창 띄우기
			// 3. 누른 횟수가 20번 이상이면,'앗. 그만 누르세요 이제!' alert 창 띄우기
    </script>

</head>

<body>
<h1>Javascript를 연습하자!</h1>

<div class="question-box">
    <h2>누른 횟수를 보여주는 함수 만들기</h2>
    <h5>1. 버튼을 누를 때마다(함수를 호출할 때마다) 누른 횟수 증가</h5>
    <h5>2. 누른 횟수가 20 미만이면 "안녕! {{실제 누른 횟수}} 번 누르셨네요!" alert 창 띄우기</h5>
    <h5>3. 누른 횟수가 20번 이상이면,'앗. 그만 누르세요 이제!' alert 창 띄우기</h5>
    <button onclick="showButtonCount()">클릭</button>
</div>
</body>

</html>

 

실습3 : 짝수 번 눌렀을 때는 "짝짝짝👏", 홀수 번 눌렀을 때는 "홀홀홀🎅" 얼럿을 띄우는 버튼을 만들기

<!DOCTYPEhtml>
<htmllang="ko">

<head>
	<metacharset="UTF-8">
	<metaname="viewport"content="width=device-width,initial-scale=1.0">
	<title>스파르타코딩클럽|자바스크립트연습</title>
	<script>
		letnum=0;
		functionhandleClick(){
			num+=1;
			alert(`${num}번눌렀습니다.`)
			if(num%2==0){//즉,짝수인경우
				alert("짝짝짝");
			}else{
				alert("홀홀홀");
		}
}
</script>
</head>

<body>
	<buttononclick="handleClick()">이버튼을눌러보세요</button>
</body>

</html>

 

실습4 : 따릉이 정보 확인하기

다음 데이터를 크롬 개발자도구에 입력후, 개발자도구에서 진행한다.

let bikes = [
  {
    rackTotCnt: "7",
    stationName: "101. (구)합정동 주민센터",
    parkingBikeTotCnt: "4",
    shared: "14",
    stationLatitude: "37.54956055",
    stationLongitude: "126.90575409",
    stationId: "ST-3",
  },
  {
    rackTotCnt: "22",
    stationName: "102. 망원역 1번출구 앞",
    parkingBikeTotCnt: "17",
    shared: "5",
    stationLatitude: "37.55564880",
    stationLongitude: "126.91062927",
    stationId: "ST-4",
  },
  {
    rackTotCnt: "16",
    stationName: "103. 망원역 2번출구 앞",
    parkingBikeTotCnt: "11",
    shared: "13",
    stationLatitude: "37.55495071",
    stationLongitude: "126.91083527",
    stationId: "ST-5",
  },
  {
    rackTotCnt: "15",
    stationName: "104. 합정역 1번출구 앞",
    parkingBikeTotCnt: "11",
    shared: "0",
    stationLatitude: "37.55062866",
    stationLongitude: "126.91498566",
    stationId: "ST-6",
  },
  {
    rackTotCnt: "7",
    stationName: "105. 합정역 5번출구 앞",
    parkingBikeTotCnt: "1",
    shared: "0",
    stationLatitude: "37.55000687",
    stationLongitude: "126.91482544",
    stationId: "ST-7",
  },
  {
    rackTotCnt: "12",
    stationName: "106. 합정역 7번출구 앞",
    parkingBikeTotCnt: "8",
    shared: "8",
    stationLatitude: "37.54864502",
    stationLongitude: "126.91282654",
    stationId: "ST-8",
  },
  {
    rackTotCnt: "7",
    stationName: "107. 신한은행 서교동금융센터점 앞",
    parkingBikeTotCnt: "5",
    shared: "14",
    stationLatitude: "37.55751038",
    stationLongitude: "126.91850281",
    stationId: "ST-9",
  },
  {
    rackTotCnt: "12",
    stationName: "108. 서교동 사거리",
    parkingBikeTotCnt: "9",
    shared: "8",
    stationLatitude: "37.55274582",
    stationLongitude: "126.91861725",
    stationId: "ST-10",
  },
  {
    rackTotCnt: "12",
    stationName: "109. 제일빌딩 앞",
    parkingBikeTotCnt: "8",
    shared: "33",
    stationLatitude: "37.54769135",
    stationLongitude: "126.91998291",
    stationId: "ST-11",
  },
  {
    rackTotCnt: "22",
    stationName: "110. 사천교",
    parkingBikeTotCnt: "16",
    shared: "5",
    stationLatitude: "37.56819916",
    stationLongitude: "126.91784668",
    stationId: "ST-13",
  },
  {
    rackTotCnt: "12",
    stationName: "111. 상수역 2번출구 앞",
    parkingBikeTotCnt: "9",
    shared: "25",
    stationLatitude: "37.54787064",
    stationLongitude: "126.92353058",
    stationId: "ST-15",
  },
  {
    rackTotCnt: "12",
    stationName: "112. 극동방송국 앞",
    parkingBikeTotCnt: "8",
    shared: "25",
    stationLatitude: "37.54920197",
    stationLongitude: "126.92320251",
    stationId: "ST-16",
  },
  {
    rackTotCnt: "27",
    stationName: "113. 홍대입구역 2번출구 앞",
    parkingBikeTotCnt: "24",
    shared: "22",
    stationLatitude: "37.55749893",
    stationLongitude: "126.92380524",
    stationId: "ST-18",
  },
  {
    rackTotCnt: "17",
    stationName: "114. 홍대입구역 8번출구 앞",
    parkingBikeTotCnt: "14",
    shared: "129",
    stationLatitude: "37.55706024",
    stationLongitude: "126.92442322",
    stationId: "ST-20",
  },
  {
    rackTotCnt: "17",
    stationName: "115. 사루비아 빌딩 앞",
    parkingBikeTotCnt: "1",
    shared: "0",
    stationLatitude: "37.55893326",
    stationLongitude: "126.92711639",
    stationId: "ST-12",
  },
  {
    rackTotCnt: "7",
    stationName: "116. 일진아이윌아파트 옆",
    parkingBikeTotCnt: "1",
    shared: "0",
    stationLatitude: "37.56454086",
    stationLongitude: "126.92707062",
    stationId: "ST-14",
  },
  {
    rackTotCnt: "27",
    stationName: "117. 홍은사거리",
    parkingBikeTotCnt: "9",
    shared: "0",
    stationLatitude: "37.59115982",
    stationLongitude: "126.94132996",
    stationId: "ST-17",
  },
  {
    rackTotCnt: "12",
    stationName: "118. 광흥창역 2번출구 앞",
    parkingBikeTotCnt: "9",
    shared: "67",
    stationLatitude: "37.54773331",
    stationLongitude: "126.93176270",
    stationId: "ST-19",
  },
  {
    rackTotCnt: "12",
    stationName: "119. 서강나루 공원",
    parkingBikeTotCnt: "9",
    shared: "17",
    stationLatitude: "37.54528427",
    stationLongitude: "126.93105316",
    stationId: "ST-21",
  },
  {
    rackTotCnt: "7",
    stationName: "120. 신수동 사거리",
    parkingBikeTotCnt: "3",
    shared: "0",
    stationLatitude: "37.54524231",
    stationLongitude: "126.93411255",
    stationId: "ST-22",
  },
  {
    rackTotCnt: "17",
    stationName: "121. 마포소방서 앞",
    parkingBikeTotCnt: "11",
    shared: "24",
    stationLatitude: "37.54976654",
    stationLongitude: "126.93317413",
    stationId: "ST-23",
  },
  {
    rackTotCnt: "12",
    stationName: "122. 신성기사식당 앞",
    parkingBikeTotCnt: "6",
    shared: "0",
    stationLatitude: "37.54745865",
    stationLongitude: "126.93837738",
    stationId: "ST-24",
  },
  {
    rackTotCnt: "22",
    stationName: "123. 문화촌 공원",
    parkingBikeTotCnt: "7",
    shared: "0",
    stationLatitude: "37.59432983",
    stationLongitude: "126.94738770",
    stationId: "ST-25",
  },
  {
    rackTotCnt: "22",
    stationName: "124. 서강대 정문 건너편",
    parkingBikeTotCnt: "7",
    shared: "0",
    stationLatitude: "37.55113983",
    stationLongitude: "126.93698883",
    stationId: "ST-26",
  },
  {
    rackTotCnt: "17",
    stationName: "125. 서강대 남문 옆",
    parkingBikeTotCnt: "13",
    shared: "0",
    stationLatitude: "37.54948425",
    stationLongitude: "126.93894958",
    stationId: "ST-27",
  },
  {
    rackTotCnt: "22",
    stationName: "126. 서강대 후문 옆",
    parkingBikeTotCnt: "17",
    shared: "5",
    stationLatitude: "37.55041122",
    stationLongitude: "126.94384766",
    stationId: "ST-28",
  },
  {
    rackTotCnt: "22",
    stationName: "128. 신촌역(2호선) 1번출구 옆",
    parkingBikeTotCnt: "14",
    shared: "5",
    stationLatitude: "37.55549622",
    stationLongitude: "126.93634033",
    stationId: "ST-30",
  },
  {
    rackTotCnt: "17",
    stationName: "129. 신촌역(2호선) 6번출구 옆",
    parkingBikeTotCnt: "8",
    shared: "0",
    stationLatitude: "37.55505371",
    stationLongitude: "126.93756866",
    stationId: "ST-31",
  },
  {
    rackTotCnt: "12",
    stationName: "130. 신촌역(2호선) 7번출구 앞",
    parkingBikeTotCnt: "8",
    shared: "17",
    stationLatitude: "37.55485916",
    stationLongitude: "126.93615723",
    stationId: "ST-32",
  },
  {
    rackTotCnt: "25",
    stationName: "131. 증산2교",
    parkingBikeTotCnt: "17",
    shared: "4",
    stationLatitude: "37.58417130",
    stationLongitude: "126.91110229",
    stationId: "ST-33",
  },
  {
    rackTotCnt: "17",
    stationName: "133. 해담는다리",
    parkingBikeTotCnt: "11",
    shared: "12",
    stationLatitude: "37.58203125",
    stationLongitude: "126.90899658",
    stationId: "ST-35",
  },
  {
    rackTotCnt: "10",
    stationName: "134. 연세로 명물길",
    parkingBikeTotCnt: "6",
    shared: "20",
    stationLatitude: "37.55789185",
    stationLongitude: "126.93807983",
    stationId: "ST-36",
  },
  {
    rackTotCnt: "12",
    stationName: "135. 명물길 원형무대 앞",
    parkingBikeTotCnt: "10",
    shared: "0",
    stationLatitude: "37.55910110",
    stationLongitude: "126.93917847",
    stationId: "ST-37",
  },
  {
    rackTotCnt: "9",
    stationName: "136. 대흥동 주민센터",
    parkingBikeTotCnt: "1",
    shared: "11",
    stationLatitude: "37.55600357",
    stationLongitude: "126.94229889",
    stationId: "ST-38",
  },
  {
    rackTotCnt: "12",
    stationName: "137. NH농협 신촌지점 앞",
    parkingBikeTotCnt: "4",
    shared: "0",
    stationLatitude: "37.55681229",
    stationLongitude: "126.94318390",
    stationId: "ST-39",
  },
  {
    rackTotCnt: "12",
    stationName: "138. 신촌동 제1공영주차장 앞",
    parkingBikeTotCnt: "7",
    shared: "25",
    stationLatitude: "37.55917740",
    stationLongitude: "126.93452454",
    stationId: "ST-40",
  },
  {
    rackTotCnt: "15",
    stationName: "139. 연세대 정문 건너편",
    parkingBikeTotCnt: "13",
    shared: "7",
    stationLatitude: "37.55979538",
    stationLongitude: "126.93447876",
    stationId: "ST-43",
  },
  {
    rackTotCnt: "22",
    stationName: "140. 이화여대 후문",
    parkingBikeTotCnt: "6",
    shared: "0",
    stationLatitude: "37.56000900",
    stationLongitude: "126.94073486",
    stationId: "ST-41",
  },
  {
    rackTotCnt: "22",
    stationName: "141. 연대 대운동장 옆",
    parkingBikeTotCnt: "13",
    shared: "5",
    stationLatitude: "37.56238174",
    stationLongitude: "126.93264771",
    stationId: "ST-42",
  },
  {
    rackTotCnt: "13",
    stationName: "142. 아현역 4번출구 앞",
    parkingBikeTotCnt: "1",
    shared: "0",
    stationLatitude: "37.55720139",
    stationLongitude: "126.95566559",
    stationId: "ST-200",
  },
  {
    rackTotCnt: "11",
    stationName: "143. 공덕역 2번출구",
    parkingBikeTotCnt: "7",
    shared: "0",
    stationLatitude: "37.54457855",
    stationLongitude: "126.95021820",
    stationId: "ST-201",
  },
  {
    rackTotCnt: "12",
    stationName: "144. 공덕역 8번출구",
    parkingBikeTotCnt: "6",
    shared: "0",
    stationLatitude: "37.54357910",
    stationLongitude: "126.95132446",
    stationId: "ST-202",
  },
  {
    rackTotCnt: "11",
    stationName: "145. 공덕역 5번출구",
    parkingBikeTotCnt: "8",
    shared: "36",
    stationLatitude: "37.54425049",
    stationLongitude: "126.95163727",
    stationId: "ST-203",
  },
  {
    rackTotCnt: "14",
    stationName: "146. 마포역 2번출구 뒤",
    parkingBikeTotCnt: "6",
    shared: "0",
    stationLatitude: "37.53993607",
    stationLongitude: "126.94582367",
    stationId: "ST-204",
  },
  {
    rackTotCnt: "9",
    stationName: "147. 마포역 4번출구 뒤",
    parkingBikeTotCnt: "4",
    shared: "0",
    stationLatitude: "37.53927231",
    stationLongitude: "126.94591522",
    stationId: "ST-205",
  },
  {
    rackTotCnt: "17",
    stationName: "150. 서강대역 2번출구 앞",
    parkingBikeTotCnt: "13",
    shared: "65",
    stationLatitude: "37.55295563",
    stationLongitude: "126.93434143",
    stationId: "ST-207",
  },
  {
    rackTotCnt: "12",
    stationName: "151. 망원1동주민센터",
    parkingBikeTotCnt: "11",
    shared: "58",
    stationLatitude: "37.55568695",
    stationLongitude: "126.90554810",
    stationId: "ST-208",
  },
  {
    rackTotCnt: "32",
    stationName: "152. 마포구민체육센터 앞",
    parkingBikeTotCnt: "8",
    shared: "31",
    stationLatitude: "37.55661011",
    stationLongitude: "126.89801788",
    stationId: "ST-209",
  },
  {
    rackTotCnt: "12",
    stationName: "153. 성산2교 사거리",
    parkingBikeTotCnt: "7",
    shared: "17",
    stationLatitude: "37.56469727",
    stationLongitude: "126.91261292",
    stationId: "ST-210",
  },
  {
    rackTotCnt: "15",
    stationName: "154. 마포구청역 ",
    parkingBikeTotCnt: "9",
    shared: "0",
    stationLatitude: "37.56090927",
    stationLongitude: "126.90549469",
    stationId: "ST-211",
  },
  {
    rackTotCnt: "17",
    stationName: "155. 가좌역1 번출구 뒤",
    parkingBikeTotCnt: "14",
    shared: "0",
    stationLatitude: "37.56855011",
    stationLongitude: "126.91451263",
    stationId: "ST-212",
  },
  {
    rackTotCnt: "12",
    stationName: "156. 서울서부지방법원 앞",
    parkingBikeTotCnt: "9",
    shared: "0",
    stationLatitude: "37.54990387",
    stationLongitude: "126.95514679",
    stationId: "ST-213",
  },
  {
    rackTotCnt: "14",
    stationName: "157. 애오개역 4번출구 앞",
    parkingBikeTotCnt: "1",
    shared: "0",
    stationLatitude: "37.55300140",
    stationLongitude: "126.95668793",
    stationId: "ST-214",
  },
  {
    rackTotCnt: "17",
    stationName: "158. 독립문 어린이 공원",
    parkingBikeTotCnt: "1",
    shared: "0",
    stationLatitude: "37.57125854",
    stationLongitude: "126.96047974",
    stationId: "ST-215",
  },
  {
    rackTotCnt: "9",
    stationName: "159. 이대역 4번 출구",
    parkingBikeTotCnt: "1",
    shared: "0",
    stationLatitude: "37.55695343",
    stationLongitude: "126.94634247",
    stationId: "ST-216",
  },
  {
    rackTotCnt: "22",
    stationName: "160. 북아현동 가구거리",
    parkingBikeTotCnt: "15",
    shared: "0",
    stationLatitude: "37.55754852",
    stationLongitude: "126.95938110",
    stationId: "ST-217",
  },
  {
    rackTotCnt: "10",
    stationName: "161. 무악재역1번 출구",
    parkingBikeTotCnt: "0",
    shared: "0",
    stationLatitude: "37.58224487",
    stationLongitude: "126.95064545",
    stationId: "ST-218",
  },
  {
    rackTotCnt: "17",
    stationName: "162. 봉원고가차도 밑",
    parkingBikeTotCnt: "8",
    shared: "0",
    stationLatitude: "37.56526947",
    stationLongitude: "126.94624329",
    stationId: "ST-219",
  },
  {
    rackTotCnt: "9",
    stationName: "163. 명지전문대학교 정문 앞",
    parkingBikeTotCnt: "0",
    shared: "0",
    stationLatitude: "37.58369827",
    stationLongitude: "126.92496490",
    stationId: "ST-220",
  },
  {
    rackTotCnt: "12",
    stationName: "164. 북가좌1동 주민센터 ",
    parkingBikeTotCnt: "7",
    shared: "25",
    stationLatitude: "37.57447815",
    stationLongitude: "126.91004944",
    stationId: "ST-221",
  },
  {
    rackTotCnt: "22",
    stationName: "165. 중앙근린공원",
    parkingBikeTotCnt: "9",
    shared: "0",
    stationLatitude: "37.57513809",
    stationLongitude: "126.91394043",
    stationId: "ST-222",
  },
  {
    rackTotCnt: "22",
    stationName: "166. 가재울 초등학교",
    parkingBikeTotCnt: "6",
    shared: "0",
    stationLatitude: "37.57327652",
    stationLongitude: "126.91967773",
    stationId: "ST-223",
  },
  {
    rackTotCnt: "17",
    stationName: "167. 연가초등학교 옆",
    parkingBikeTotCnt: "12",
    shared: "0",
    stationLatitude: "37.57946014",
    stationLongitude: "126.91712952",
    stationId: "ST-224",
  },
  {
    rackTotCnt: "17",
    stationName: "169. 북가좌 삼거리",
    parkingBikeTotCnt: "6",
    shared: "0",
    stationLatitude: "37.57300186",
    stationLongitude: "126.90779877",
    stationId: "ST-226",
  },
  {
    rackTotCnt: "12",
    stationName: "170. 가재울 뉴타운 주유소 옆",
    parkingBikeTotCnt: "9",
    shared: "33",
    stationLatitude: "37.57311249",
    stationLongitude: "126.92244720",
    stationId: "ST-227",
  },
  {
    rackTotCnt: "12",
    stationName: "171. 임광빌딩 앞",
    parkingBikeTotCnt: "9",
    shared: "8",
    stationLatitude: "37.56472397",
    stationLongitude: "126.96727753",
    stationId: "ST-228",
  },
  {
    rackTotCnt: "10",
    stationName: "173. 서대문역 8번출구 앞",
    parkingBikeTotCnt: "4",
    shared: "0",
    stationLatitude: "37.56477737",
    stationLongitude: "126.96614838",
    stationId: "ST-230",
  },
  {
    rackTotCnt: "22",
    stationName: "175. 홍연2교옆",
    parkingBikeTotCnt: "6",
    shared: "0",
    stationLatitude: "37.57807159",
    stationLongitude: "126.93081665",
    stationId: "ST-231",
  },
  {
    rackTotCnt: "12",
    stationName: "176. 명지대학교 도서관",
    parkingBikeTotCnt: "0",
    shared: "0",
    stationLatitude: "37.58109665",
    stationLongitude: "126.92402649",
    stationId: "ST-555",
  },
  {
    rackTotCnt: "10",
    stationName: "177. 북가좌 초등학교",
    parkingBikeTotCnt: "1",
    shared: "0",
    stationLatitude: "37.57767487",
    stationLongitude: "126.90980530",
    stationId: "ST-345",
  },
  {
    rackTotCnt: "12",
    stationName: "178. 증산3교 앞",
    parkingBikeTotCnt: "0",
    shared: "0",
    stationLatitude: "37.57987595",
    stationLongitude: "126.90634918",
    stationId: "ST-349",
  },
  {
    rackTotCnt: "17",
    stationName: "179. 가좌역 4번출구 앞",
    parkingBikeTotCnt: "14",
    shared: "47",
    stationLatitude: "37.56912231",
    stationLongitude: "126.91479492",
    stationId: "ST-232",
  },
  {
    rackTotCnt: "12",
    stationName: "180. 충정로역 7번출구 아래",
    parkingBikeTotCnt: "10",
    shared: "8",
    stationLatitude: "37.55996704",
    stationLongitude: "126.96246338",
    stationId: "ST-233",
  },
  {
    rackTotCnt: "17",
    stationName: "181. 망원초록길 입구",
    parkingBikeTotCnt: "9",
    shared: "0",
    stationLatitude: "37.55134201",
    stationLongitude: "126.90267181",
    stationId: "ST-339",
  },
  {
    rackTotCnt: "12",
    stationName: "182. 망원2빗물펌프장 앞",
    parkingBikeTotCnt: "7",
    shared: "0",
    stationLatitude: "37.55156708",
    stationLongitude: "126.90284729",
    stationId: "ST-340",
  },
  {
    rackTotCnt: "17",
    stationName: "183. 하늘채코오롱아파트 건너편",
    parkingBikeTotCnt: "10",
    shared: "0",
    stationLatitude: "37.56516647",
    stationLongitude: "126.91939545",
    stationId: "ST-341",
  },
  {
    rackTotCnt: "11",
    stationName: "184. SK망원동주유소 건너편",
    parkingBikeTotCnt: "4",
    shared: "0",
    stationLatitude: "37.55894852",
    stationLongitude: "126.90775299",
    stationId: "ST-342",
  },
  {
    rackTotCnt: "17",
    stationName: "185. 마포 신수공원 앞",
    parkingBikeTotCnt: "5",
    shared: "0",
    stationLatitude: "37.54254532",
    stationLongitude: "126.93429565",
    stationId: "ST-343",
  },
  {
    rackTotCnt: "42",
    stationName: "186. 월드컵공원",
    parkingBikeTotCnt: "22",
    shared: "10",
    stationLatitude: "37.56396484",
    stationLongitude: "126.89820862",
    stationId: "ST-344",
  },
  {
    rackTotCnt: "12",
    stationName: "188. 홍은동 정원여중 입구",
    parkingBikeTotCnt: "2",
    shared: "0",
    stationLatitude: "37.58638763",
    stationLongitude: "126.93512726",
    stationId: "ST-346",
  },
  {
    rackTotCnt: "12",
    stationName: "191. 서우빌딩(바른학원)",
    parkingBikeTotCnt: "6",
    shared: "0",
    stationLatitude: "37.57889175",
    stationLongitude: "126.91073608",
    stationId: "ST-347",
  },
  {
    rackTotCnt: "12",
    stationName: "192. 연서어린이공원",
    parkingBikeTotCnt: "0",
    shared: "0",
    stationLatitude: "37.57222748",
    stationLongitude: "126.92306519",
    stationId: "ST-348",
  },
  {
    rackTotCnt: "12",
    stationName: "194. 증산교 앞",
    parkingBikeTotCnt: "2",
    shared: "0",
    stationLatitude: "37.57731628",
    stationLongitude: "126.90296936",
    stationId: "ST-350",
  },
  {
    rackTotCnt: "12",
    stationName: "195. 모래내고가차도 ",
    parkingBikeTotCnt: "6",
    shared: "42",
    stationLatitude: "37.56765747",
    stationLongitude: "126.91780853",
    stationId: "ST-351",
  },
  {
    rackTotCnt: "12",
    stationName: "196. 연희교차로 인근",
    parkingBikeTotCnt: "1",
    shared: "0",
    stationLatitude: "37.56612015",
    stationLongitude: "126.92589569",
    stationId: "ST-352",
  },
  {
    rackTotCnt: "17",
    stationName: "198. 충정2교",
    parkingBikeTotCnt: "15",
    shared: "0",
    stationLatitude: "37.56213760",
    stationLongitude: "126.96377563",
    stationId: "ST-354",
  },
  {
    rackTotCnt: "32",
    stationName: "199. 서울 월드컵 경기장",
    parkingBikeTotCnt: "7",
    shared: "0",
    stationLatitude: "37.56684494",
    stationLongitude: "126.89644623",
    stationId: "ST-443",
  },
  {
    rackTotCnt: "22",
    stationName: "200. 국회의원회관",
    parkingBikeTotCnt: "8",
    shared: "0",
    stationLatitude: "37.52841568",
    stationLongitude: "126.91391754",
    stationId: "ST-45",
  },
  {
    rackTotCnt: "17",
    stationName: "201. 진미파라곤 앞",
    parkingBikeTotCnt: "9",
    shared: "6",
    stationLatitude: "37.53123856",
    stationLongitude: "126.92133331",
    stationId: "ST-46",
  },
  {
    rackTotCnt: "32",
    stationName: "202. 국민일보 앞",
    parkingBikeTotCnt: "21",
    shared: "19",
    stationLatitude: "37.52881622",
    stationLongitude: "126.92453003",
    stationId: "ST-47",
  },
  {
    rackTotCnt: "17",
    stationName: "203. 국회의사당역 3번출구 옆",
    parkingBikeTotCnt: "14",
    shared: "76",
    stationLatitude: "37.52805710",
    stationLongitude: "126.91870117",
    stationId: "ST-51",
  },
  {
    rackTotCnt: "15",
    stationName: "204. 국회의사당역 5번출구 옆",
    parkingBikeTotCnt: "10",
    shared: "53",
    stationLatitude: "37.52816391",
    stationLongitude: "126.91702271",
    stationId: "ST-50",
  },
  {
    rackTotCnt: "22",
    stationName: "205. 산업은행 앞",
    parkingBikeTotCnt: "13",
    shared: "0",
    stationLatitude: "37.52626419",
    stationLongitude: "126.92050934",
    stationId: "ST-52",
  },
  {
    rackTotCnt: "37",
    stationName: "206. KBS 앞",
    parkingBikeTotCnt: "24",
    shared: "11",
    stationLatitude: "37.52466583",
    stationLongitude: "126.91802216",
    stationId: "ST-53",
  },
  {
    rackTotCnt: "42",
    stationName: "207. 여의나루역 1번출구 앞",
    parkingBikeTotCnt: "16",
    shared: "0",
    stationLatitude: "37.52698898",
    stationLongitude: "126.93209839",
    stationId: "ST-73",
  },
  {
    rackTotCnt: "14",
    stationName: "209. 유진투자증권빌딩 앞",
    parkingBikeTotCnt: "12",
    shared: "14",
    stationLatitude: "37.52461243",
    stationLongitude: "126.92783356",
    stationId: "ST-55",
  },
  {
    rackTotCnt: "23",
    stationName: "210. IFC몰",
    parkingBikeTotCnt: "16",
    shared: "13",
    stationLatitude: "37.52606583",
    stationLongitude: "126.92553711",
    stationId: "ST-56",
  },
  {
    rackTotCnt: "15",
    stationName: "211. 여의도역 4번출구 옆",
    parkingBikeTotCnt: "2",
    shared: "0",
    stationLatitude: "37.52222824",
    stationLongitude: "126.92463684",
    stationId: "ST-57",
  },
  {
    rackTotCnt: "37",
    stationName: "212. 여의도역 1번출구 옆",
    parkingBikeTotCnt: "9",
    shared: "0",
    stationLatitude: "37.52136230",
    stationLongitude: "126.92346191",
    stationId: "ST-58",
  },
];

 

질문1 : 5대 이하 정류장의 이름 출력

for (let i = 0  ; i < bikes.length ; i++) {
    if (bikes[i]["parkingBikeTotCnt"] <= 5) {
        console.log(bikes[i]["stationName"]);
                    }
}

질문2 : 입력한 자전거 대수 이하 정류장의 이름을 출력하는 함수를 만들어주세요.

    function checkcount(countNum) {
        for (let i = 0; i < bikes.length; i++) {
            if (bikes[i]["parkingBikeTotCnt"] <= countNum) {
                let station = bikes[i]["stationName"];
                console.log("현재 " + countNum + "대 이하 정류장은 : " + station + " 입니다.")
            }
        }

    }

 

 

실습5 : 입력받은 칸이 빈 칸이면 경고 메시지를 띄우고, 그렇지 않으면 alert(입력값) 띄우기

<!Doctype html>
<html lang="ko">

<head>
    <meta charset="UTF-8">
    <title>jQuery 연습하고 가기!</title>

    <!-- JQuery를 import 합니다 -->
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>

    <style type="text/css">
        div.question-box {
            margin: 10px 0 20px 0;
        }
    </style>

    <script>
        function isEmpty() {
            let tempChar = $('#input-value').val();
            if (tempChar != '') {
                alert(tempChar);
            } else {
                alert("글자를 입력해 주세요")
            }

        }
    </script>

</head>

<body>
<h1>jQuery + Javascript의 조합을 연습하자!</h1>

<div class="question-box">
    <h2>빈칸 체크 함수 만들기</h2>
    <h5>1-1. 버튼을 눌렀을 때 입력한 글자로 얼럿 띄우기</h5>
    <h5>[완성본]1-2. 버튼을 눌렀을 때 칸에 아무것도 없으면 "입력하세요!" 얼럿 띄우기</h5>
    <input id="input-value" type="text"/>
    <button onclick="isEmpty()">클릭</button>
</div>
</body>

</html>

 

실습6 : 입력받은 이메일 형식이 유효한지 확인하기 (난이도 : ⭐️⭐️⭐️⭐️)

<!Doctype html>
<html lang="ko">

<head>
    <meta charset="UTF-8">
    <title>jQuery 연습하고 가기!</title>

    <!-- JQuery를 import 합니다 -->
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>

    <style type="text/css">
        div.question-box {
            margin: 10px 0 20px 0;
        }
    </style>

    <script>
        function validateEmail() {
            let mailAddress = $('#input-email').val();
            if (mailAddress.includes('@')) {
                let domain = mailAddress.split('@')[1].split('.')[0];
                alert(domain);
            } else {
                alert('이메일 주소 형식이 아니네용?')

            }
            // 1. input-email 값을 가져온다.
            // 2. 만약 가져온 값에 @가 있으면 (includes 이용하기 - 찾아보자!)
            // 3. contact@gmail.com -> gmail 만 추출해서
            // 4. alert(도메인 값);으로 띄우기
            // 5. 만약 이메일이 아니면 '이메일이 아닙니다.' 라는 얼럿 띄우기
        }

    </script>

</head>

<body>
<h1>jQuery + Javascript의 조합을 연습하자!</h1>

<div class="question-box">
    <h2>이메일 판별 함수 만들기</h2>
    <h5>1. 버튼을 눌렀을 때 입력받은 이메일로 얼럿 띄우기</h5>
    <h5>2. 이메일이 아니면(@가 없으면) '이메일이 아닙니다'라는 얼럿 띄우기</h5>
    <h5>[완성본]3. 이메일 도메인만 얼럿 띄우기</h5>
    <input id="input-email" type="text"/>
    <button onclick="validateEmail()">클릭</button>
</div>
</body>

</html>

 

 

 

 

Ajax 통신과 API (application progamming interface)

ajax는 자바스크립트로 클라이언트와 서버가 통신하는 방식이다.  클라이언트가 서버에 데이터를 요청하고, 서버가 응답한 데이터를 받아 조작할 수 있다. 이 때 데이터 통신을 수행할 때 api를 통해서 수행한다.

 

API는 특정 프로그램을 쓰고 싶을 때 접근하는 인터페이스, 규칙이다. 서버쪽에서 api를 만들어 놓으며, 클라이언트는 이 api에 맞춰서 클라이언트 단으로 요청한다. 즉, 해당 데이터를 외부에서 접근할 수 있게끔 하나의 통로를 만든 것이며 규칙이 있다.

 

기본적으로 api는 http 주소이다. 아래 예시는 서울시 오픈API 중 하나인 실시간 대기 환경정보 API이다.

http://openapi.seoul.go.kr:8088/6d4d776b466c656533356a4b4b5872/json/RealtimeCityAir/1/99

 

일반 텍스트로 복잡하게 나온다면, jsonview라는 크롬 확장 프로그램을 설치하면 스샷과 같이 보기좋게 볼 수 있다. 사실상 필수.

https://chrome.google.com/webstore/detail/jsonview/chklaanhfefbnpoihckbnefhakgolnmc?hl=ko

 

이러한 URL을 접속한 것 자체가, 서버에 접속해서 데이터를 요청하는 행위이다.

 

# 참고 : 해당 API 페이지에서 -를 누르면 내용을 숨기거나 열 수 있다.

 

API 상세정보 확인하기

이 내용들은 api 제작처에서 정의하는 내용이다. 이것은  서울시에서 정의한것이다. 

http://openapi.seoul.go.kr:8088/6d4d776b466c656533356a4b4b5872/json/RealtimeCityAir/1/99

6d4d 로 시작하는 건 인증키이다.

/json 은 json방식으로 가져오라는 것.

realtimeCityAir 은 해당 api의 이름

 / 1/99 는 1~99페이지부터 가져오라는것.

 

더 자세히 알기 위해, 서울시 api 자료를 보면 어떻게 api 접근할지 나와있다이 api문서를 보면, 어떻게 사용하는지 무슨 정보를 쓰는지 어떻게 받는지 등을 확인할 수 있다. 사실 API를 쓰기 전는 당연히 이러한 문서를 봐야 한다.

http://data.seoul.go.kr/dataList/OA-15493/A/1/datasetView.do

 

 

인증키는 따로 신청해서 받아야 함.

맨뒤에 /1/5 는 1페이지부터 5페이지까지.

 api는 무료 유료가 있고, 무료는 데이터 받아오는데 시간이 좀 걸릴 수 있으니 업데이트 하고 조금 기다려야 한다.

 

 

 

 

여러가지 API

API를 를 통해, 데이터를 가져다 쓸 수 있거나 기능을 편하게 사용할 수 있다. 대표적인 OpenAPI(공개API)로는 아래 예시와 함께 여러가지가 있다.

 

구글 API, 네이버 API, 카카오 API

서울 열린 데이터 광장(https://data.seoul.go.kr/),

공공데이터 포털(https://www.data.go.kr/)

경기도 오픈API

https://data.gg.go.kr/portal/data/service/selectServicePage.do?page=1&rows=10&sortColumn=&sortDirection=&infId=6RMUFTKZE80QXD2RHD1F30243579&infSeq=2&order=&loc=

무료로 사용할 수 있는 API 모음(https://github.com/public-apis/public-apis)

 

json 데이터 형식

데이터 형식을 여러가지 종류가 있다. xml, excel, json 등등 이 중 json 형식은 실시간 데이터 형식에 많이 사용된다. 그 외에도 여러 용도가 많다. key:value 형태의 데이터 포맷이다. 자바스크립트 딕셔너리와 매우 유사하다. 위의 서울시 대기환경정보 내용의 형식이 json 형식이며, 딕셔너리와 리스트가 조합된 형태이다. 이렇게 "구조화"된 데이터를 주고받는다.

 

여기서는, row라는 key 값에 리스트 형태의 딕셔너리들이 value로써 들어가있다. 여기서 특정 데이터를 접근하려면 어떻게 해야할까?

 

일단,  RealtimeCityAir 는 {}중괄호를 사용하므로딕셔너리이다.

그 아래에는 list_total_count , RESULT, row 라는 총 3가지의 key값이 있다.

list_total_count라는 key는 정수형 값인 25라는 value로 끝난다.

RESULT라는 KEY는 딕셔너리형 값으로써, CODE, MESSAGE 라는 KEY 2개를 가진다.

row라는 키는 []대괄호를 사용하므로 리스트형 값이며, 그 리스트에 딕셔너리형이 들어가있는 형태를 가진다. 

 

다시 정리하면, RealtimeCityAir의 row의 0번째리스트의 MSRSTE_NM 값은 "중구" 가 된다.

 

 

 

API에서 클라이언트 <-> 서버 요청 모델

서버가 열어놓은 API에 클라이언트가 연결하여 요청을 하고 응답을 받는다.  API는 은행창구와 비슷하며, 기업고객/개인고객, 대출/예금 등 다르듯이 요청 방식이 정해져 있고 그 방식에 따라 다르게 동작한다.

 

API의 요청방식은 많은 종류가 있지만 대표적으로 GET와 POST가 있다. 또 다른 방식들은 아래 링크에서 자세히 확인할 수 있다.

https://developer.mozilla.org/ko/docs/Web/HTTP/Methods

 

HTTP 요청 메서드

HTTP는 요청 메서드를 정의하여, 주어진 리소스에 수행하길 원하는 행동을 나타냅니다. 간혹 요청 메서드를 "HTTP 동사"라고 부르기도 합니다. 각각의 메서드는 서로 다른 의미를 구현하지만, 일부

developer.mozilla.org

브라우저는 http라는 프로토콜을 따른다. http는 클라이언트와 서버가 데이터를 서로 전송하기 위한 규칙이라고 이해하면 된다. 즉 GET, POST method들은 http 프로토콜의 방식 종류이다. http 통신에서 클라이언트는 서버의 api에게 request를 보내고, 데이터들(HTML, CSS, JS) 등을 response 받게 된다.

 

GET : 통상적으로 단순히 클라이언트가 서버에 접근하여 데이터를 조회할 때 사용

POST  : 통상적으로 서버에 데이터 생성,변경,삭제를 요청할 때 사용. 서버에 데이터가 변경된다.

 

여기서 "통상적으로" 라는 것은, 사실 GET과 POST가 역할이 확실하게 정해져있다기 보단 통상적으로 데이터 조회를 할 때는 GET을 쓰기 때문에 그렇게 썼다. 그렇다면 이것을 누가 정하는가? 클라이언트 프로그래머와 서버 프로그래머가 미리 정해두는 것이다.

 

 

GET 톺아보기

통상적으로, 데이터를 조회(read) 할 때 사용된다. 예를들어, 영화 목록 조회라던지..

내가 어디 사이트에 들어갈려고 링크를 클릭하면, 그 행위가 http 통신 규약을 지켜서 GET 요청을 한 것이다.  다음 2가지 예시 주소를 보자. 이 주소에서 중요한 것은 ? 와 & 이다.

 

 

? : 여기서부터 전달할 데이터가 작성된다는 의미 (전달할 데이터는 항상 key=value 값 형태이다. 예 : google.com?q=북극곰)

& : 전달할 데이터가 더 있다는 의미 

 

https://  - https 통신규약으로 통신한다.

movie.naver.com - 네이버 영화 주소로 들어간다. 요게 은행주소라고 생각하면 됨.

/movie/bi/mi/basic.nhn - 물음표 전까지이며, 이게 api 주소이다. 요게 창구이름이라고 생각하면 됨

code=161967 - code라는 key로, value 171967을 가지고 온 것이다. 만약 code가 아니라, 임의로 moviecode 로 하면? 안된다. 사전에 서버에서 code를 정의한 것이기 때문이다.

 

google.com - 구글 주소

/search - search api에 다음 정보를 전달한다.

q=해리포터 : 검색어 q의 값은 해리포터이다.

sourceid=chrome : 브라우저 정보 sourceid의 값은 chrome

ie=UTF-8 : 인코딩 정보 ie의 값은 UTF-8 이다.

 

이런식이다. 여기서 q가 뭔지, code가 뭔지, sourceid가 뭔지 이런것을 모두 서버가 정하는 것이다. 이런게 api이며 약속이다. 만약 서버단에서 정의하지 않은 값을 쓴다면, 예를들어 q 대신 qwer=해리포터 이런식으로 하면 서버에서 인식이 안되므로 (혹시나;; 서버단에서 qwer를 정의했을 수도 있겠지만 그건 제외) 그냥 주소에서 제외되게 된다.

 

다시 정리하면, 같은 서버주소와 api라도 값을 다르게 주면 결과가 다르게 나온다는 것을 이해할 수 있다.

 

 

# 다시한번 : code라는 이름으로 영화번호 데이터를 전달해주자라는것은 누가 정하는가?

프론트엔드 개발자와 백엔드 개발자가 미리 정해둔 약속이다. 

- 백엔드 : 서버는 code로 영화번호 정보를 받는다고 정할게.

- 프론트엔드 : ㅇㅋ 그러면 클라이언트에서는 code라는 이름으로 영화번호를 넣어서 요청할게.

 

 

post란?

- 통상적으로, 데이터 생성/변경/삭제 (create/update/delete) 요청을 할 때 사용

- 예를들어, 회원가입, 회원탈퇴, 비밀번호 수정 등

- get와는 다르게 데이터를 전달할 때 주소에는 보이지 않으며, html body에 key:value 형태로 전달한다

 

 

서버 통신방식

여기서 서버 통신은, 위에서 http의 request - response 를 말하는 것이다. 이러한 방식은 동기/비동기 방식으로 나뉜다.

 

- 동기 : 작업을 하나 하고, 그 작업이 다 끝나야만 다음 작업을 할 수 있다. 

- 비동기 : 작업 요청을 하고 결과가 오지 않아도 다른 작업을 처리. 즉 병렬 처리이다. 자원을 효율적으로 활용할 수 있다.

동기 방식은 반드시 1이 끝나야 2를 실행하고, 2가 끝나면 3을 실행하는 방식이다. 즉 시간이 많이 걸린다. 비동기 방식은 1,2,3,4를 동시에 수행한다. 동기방식인 경우 클라이언트가 서버에 연결해서 뭔가를 하고 있는데, 서버에서 페이지를 업데이트해버리면 문제가 생길 수 있다.

 

ajax는 비동기방식을 사용한다. ajax는 서버에 요청해놓고 응답 오기 전까지 다른 작업을 처리한다. 응답이 오면, 그 때 정해놓은 함수 (콜백함수 ; callback function)를 호출해 실행한다. 

 

비동기 방식 덕분에 ajax를 사용하면 웹페이지 전체를 새로고침하지 않아도 작업을 수행할 수 있다. 즉 동작(event)가 일어났을 때, 전체 페이지가 아닌 일부분만 업데이트 할 수 있는 것.

 

 

 

ajax 사용하기

 

사전준비

ajax는 여러가지 방법으로 사용할 수 있는데, 그중 하나는 jquery를 사용하는 것이다. jquery에 ajax를 쓸수 있게 구현되어 있다. jquery를 사용하여 ajax를 해당 페이지에 import해야 한다. css와 동일한 방식이다.

 

페이지의 <head> 부분에 아래 부분을 넣는다.

<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>

 

만약 ajax가 import 되지 않으면, 다음과 같이 메시지가 뜨게 된다.

# 참고 : jquery가 min 버전이 아닌 slim 버전이면 ajax가 포함되어 있지 않다. 그래서 min으로 할 것.

 

 

사용하기 : 구문

다음과 같이 type, url, data, success 4가지 요소로 이루어져 있다. 

$.ajax({
  type: "GET", // GET 방식으로 요청한다.
  url: "http://openapi.seoul.go.kr:8088/6d4d776b466c656533356a4b4b5872/json/RealtimeCityAir/1/99",
  data: {}, // 요청하면서 함께 줄 데이터 (GET 요청시엔 비워두세요)
  success: function(response){ // 서버에서 준 결과를 response라는 변수에 담음
    console.log(response) // 서버에서 준 결과를 이용해서 나머지 코드를 작성
  }
})

 

 

type : GET, POST 등  요청방식이다.

url  : 접속할 서버 경로이다. api 주소를 넣는다. 또한 여기다가 추가로 ?나 &등을 포함해서 확장할 수 있다.

data :  get 방식은 주소 뒤에 요청할 방식,데이터등을 명시한다. 따라서 data에 아무것도 안씀. get 말고 post방식할 때 쓴다. get 방식은 요청 데이터가 없다. 주소 안에 ? 나 & 로 데이터들이 주소에 포함됨.

success : 성공했다면, 이런 걸 해라. 즉 요청한 결과를 어떻게 처리할 지 우리가 코딩하는 것이다. 

 

success에서, 기본적으로 해당 api에서 가져온 결과 데이터는, response에 들어간다. 이 값은 당연히 "딕셔너리" 이다. 이 딕셔너리 안에 딕셔너리가 또있고 리스트도 있다. 이런 것을 복합 딕셔너리라고 한다.

 

success는 그것을 사용하는 function을 만들고 실행하는 것이다. 즉 위 예시대로 입력하면, 아래 내용 전체가 response에 들어가는것이다. 그래서 console.log(response)로 출력하면 아래내용이 그대로 나온다. 

콘솔에서 해당 구문을 입력하면 동일하게 나오는 것을 확인할 수 있다.

 

참고로 실무에서는 이런 구문을 일일이 다 치기 보다는 복붙해서 수정해서 사용한다.

 

 

사용하기 : 내용 추출하기

위 예시에는 response에 서울시 대기 데이터 전체가 들어갔는데, 이 중 특정 데이터만 딱 뽑아보자. 

success: function(response){
    console.log(response['RealtimeCityAir']['row'][0]['MSRSTE_NM'])
  }

이렇게 하면, 결과는 딱 "중구" 만 나온다. 이것과 반복문, if문을 잘하면 끝나는 것이다.

 

 

사용하기 : 예시

 

도봉구의 미세먼지값을 가져오기

$.ajax({
  type: "GET",
  url: "http://openapi.seoul.go.kr:8088/6d4d776b466c656533356a4b4b5872/json/RealtimeCityAir/1/99",
  data: {},
  success: function(response){
		// 도봉구의 미세먼지 값만 가져와보기
		let dobong = response["RealtimeCityAir"]["row"][11];
		let guName = dobong['MSRSTE_NM'];
		let guMise = dobong['PM10'];
		console.log(guName, guMise);
  }
})

 

모든 구의 미세먼지값을 가져오기

$.ajax({
  type: "GET",
  url: "http://openapi.seoul.go.kr:8088/6d4d776b466c656533356a4b4b5872/json/RealtimeCityAir/1/99",
  data: {},
  success: function (response) {
    let cityAir = response["RealtimeCityAir"]["row"];
    for (let i = 0; i < cityAir.length; i++) {
      let mise = cityAir[i];
      let guName = mise["MSRSTE_NM"];
      let guMise = mise["PM10"];
      console.log(guName, guMise);
    }
  },
});

 

 

 

실습

 

문제1 : 버튼을 누르면 서울시 미세먼지 데이터를 새로고침하고 전체를 보여준다. 또한 미세먼지 수치가 10이상인 곳은 빨갛게 표시한다.

 

API

http://openapi.seoul.go.kr:8088/6d4d776b466c656533356a4b4b5872/json/RealtimeCityAir/1/99

 

API 설명 페이지

https://data.seoul.go.kr/dataList/OA-2219/A/1/datasetView.do;jsessionid=7106F33493DD3E59FEEB87B59FDEA12F.new_portal-svr-11

 

<!Doctype html>
<html lang="ko">

<head>
    <meta charset="UTF-8">
    <title>jQuery+Ajax의 조합을 연습하자!</title>

    <!-- jQuery를 import 합니다 -->
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>

    <style type="text/css">
        div.question-box {
            margin: 10px 0 20px 0;
        }
        .bad {
            color: red;
        }
    </style>

    <script>
        function showMiseInfo() {
            $('#mise-info').empty()   // ajax 콜 하기 전에, 싹 다 지우는것.
            $.ajax({
                type: "GET",
                url: "http://openapi.seoul.go.kr:8088/6d4d776b466c656533356a4b4b5872/json/RealtimeCityAir/1/99",
                data: {},
                success:
                    function (response) {
                    let cityAir = response["RealtimeCityAir"]["row"];
                    for (let i = 0; i < cityAir.length; i++) {
                        let mise = cityAir[i];
                        let guName = mise["MSRSTE_NM"];
                        let guMise = mise["PM10"];
                        let appendHtm = ''; // 빈값을 넣는거.. 쓰레기값이 들어가면 안되니.
                        if (guMise < 10) {
                            appendHtml = `<li>${guName} : ${guMise}</li>`
                        } else {
                            appendHtml = `<li>${guName} : <span class="bad">${guMise}</span></li>`
                        }
                         $('#mise-info').append(appendHtml);
                    }
                },
            });
        }
    </script>

</head>

<body>
<h1>jQuery+Ajax의 조합을 연습하자!</h1>

<hr/>

<div class="question-box">
    <h2> 서울시 OpenAPI(실시간 대기환경 정보)를 이용하기</h2>
    <p>모든 관측소(MSRSTE_NM)의 미세먼지 수치(PM10)를 표기해주세요</p>
    <p>업데이트 버튼을 누를 때마다 지웠다 새로 씌여져야 합니다.</p>
    <button onclick="showMiseInfo()">업데이트</button>
    <ul id="mise-info">
        <li>중구 : 82</li>
        <li>종로구 : 87</li>
        <li>용산구 : 84</li>
        <li>은평구 : 82</li>
    </ul>
</div>
</body>

</html>

 

 

문제2 : 서울시 실시간 따릉이 현황 이용 표 만들기, 추가로 사용할 수 있는 따릉이 대수가 5대 미만이면 빨간색으로 표시.

 

서울시 따릉이 API

http://openapi.seoul.go.kr:8088/6d4d776b466c656533356a4b4b5872/json/bikeList/1/99

 

API 정보

https://data.seoul.go.kr/dataList/OA-15493/A/1/datasetView.do

 

결과

 

<!Doctype html>
<html lang="ko">

<head>
    <meta charset="UTF-8">
    <title>Javascript + jQuery + Ajax 연습하기</title>
    <!-- JQuery를 import 합니다 -->
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>

    <style type="text/css">
        div.question-box {
            margin: 10px 0 20px 0;
        }

        table {
            border: 1px solid;
            border-collapse: collapse;
        }

        td,
        th {
            padding: 10px;
            border: 1px solid;
        }
        .red {
            color: red;
            font-weight: bold;
            border-color: black;
        }

    </style>

    <script>
        function showBikeInfo() {
            $('#bike-info').empty()
            $.ajax({
                type: "GET",
                url: "http://openapi.seoul.go.kr:8088/6d4d776b466c656533356a4b4b5872/json/bikeList/1/99",
                data: {},
                success:
                    function (response) {
                        let rentBike = response["rentBikeStatus"]["row"];
                        for (let i = 0; i < rentBike.length; i++) {
                            let bikeInfo = rentBike[i];
                            let bikeLoc = bikeInfo["stationName"];
                            let rackCnt = bikeInfo["rackTotCnt"];
                            let remainCnt = bikeInfo["parkingBikeTotCnt"];
                            // 위에는 변수 4개 선언을 했는데,  아래처럼 3개를 해도 된다.
                            // let bikeLoc = rentBike[i]["stationName"];
                            // let rackCnt = rentBike[i]["rackTotCnt"];
                            // let remainCnt = rentBike[i]["parkingBikeTotCnt"];
                            let bikeAppendInfo = '';
                            if (remainCnt < 5) {
                                bikeAppendInfo = `<tr><td class="red">${bikeLoc}</td><td class="red">${rackCnt}</td><td class="red">${remainCnt}</td></tr>`;
                            } else {
                                bikeAppendInfo = `<tr><td>${bikeLoc}</td><td>${rackCnt}</td><td>${remainCnt}</td></tr>`;
                            }
                            $('#bike-info').append(bikeAppendInfo);
                        }
                    },
            });

        }
    </script>

</head>

<body>
<h1>Javascript + jQuery + Ajax 연습하기</h1>

<hr/>

<div class="question-box">
    <h2>서울시 OpenAPI(실시간 따릉이 현황)를 이용하기</h2>
    <p>모든 거치대(stationName)와 거치대 수(rackTotCnt), 남은 따릉이 수(parkingBikeTotCnt)를 보여주세요</p>
    <p>업데이트 버튼을 누를 때마다 데이터가 지웠다 새로 씌여져야 합니다.</p>
    <button onclick="showBikeInfo()">업데이트</button>
    <table>
        <thead>
        <tr>
            <td>거치대 위치</td>
            <td>거치대 수</td>
            <td>현재 거치된 따릉이 수</td>
        </tr>
        </thead>
        <tbody id="bike-info">
        <tr>
            <td>102. 망원역 1번출구 앞</td>
            <td>22</td>
            <td>0</td>
        </tr>
        <tr>
            <td>103. 망원역 2번출구 앞</td>
            <td>16</td>
            <td>0</td>
        </tr>
        <tr>
            <td>104. 합정역 1번출구 앞</td>
            <td>16</td>
            <td>0</td>
        </tr>
        </tbody>
    </table>
</div>
</body>

</html>

 

참고 : 리스트의 특정 데이터를 sort 하는 함수를 이용하면, 오름차순 내림차순도 가능하다.

 

 

문제3 : 랜덤 고양이 사진 API를 활용하여 고양이 사진을 랜덤으로 받기.

 

사용할 API : https://api.thecatapi.com/v1/images/search

api 정보 : https://docs.thecatapi.com/api-reference/images/images-search'

해당 api는 접속해보고 새로고침하면, 계속 새로운 게 나옴. api를 콜 할때마다 랜덤한 고양이가 나오는 것임.

이걸 새로고침하면 계속. 바뀐다. 이 결과가 딕셔너리이므로, 해당 요소에 접근하려면 response[0][키이름] 을 하면 데이터에 접근할 수 있다.

 

<!Doctype html>

<html lang="ko">
  <head>
    <meta charset="UTF-8">
    <title>Javascript + jQuery + Ajax 연습하기</title>
    <!-- JQuery를 import 합니다 -->
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>

    <style type="text/css">
      div.question-box {
        margin: 10px 0 20px 0;
      }
      div.question-box > div {
        margin-top: 30px;
      }

    </style>

      <script>
          function showCat() {
              $.ajax ({
                  type: "GET",
                  url: "https://api.thecatapi.com/v1/images/search",
                  data: {},
                  success: function(response) {
                      let imgUrl = response[0]['url']
                      $('#img-cat').attr('src', imgUrl);
                  }
              });
          }
      </script>

  </head>
  <body>
    <h1>Javascript + jQuery + Ajax 연습하기</h1>

    <hr/>

    <div class="question-box">
      <h2>랜덤 고양이 사진 API를 이용하기</h2>
      <p>예쁜 고양이 사진을 보여주세요</p>
      <p>업데이트 버튼을 누를 때마다 화면에 데이터가 지웠다 새로 씌여져야 합니다.</p>
      <button onclick="showCat()">고양이를 보자</button>
      <div>
        <img id="img-cat" src="https://www.google.com/images/branding/googlelogo/1x/googlelogo_color_272x92dp.png"/>
      </div>
    </div>
  </body>
</html>

 

 

 

추가문제

 

문제1 : 해리포터에 나오는 주문 보기, 추가로 저주 주문은 빨간색으로 보여주기.

 

사용 할 API 요청 URL : https://www.potterapi.com/v1/spells?key=$2a$10$LiNkiQtS86DQ8.NxC9G95.NN3.KkhNa917y/RZ6EjDILkjBaAJSLS

API 정보: https://www.potterapi.com/#spell-routes

 

정답

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <title>해리 포터 주문보기</title>
    <!-- JQuery를 import 합니다 -->
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
    <style type="text/css">
        div.question-box {
            color: white;
            margin: 10px 0 20px 0;
            background-image: url('https://images.pexels.com/photos/1900185/pexels-photo-1900185.jpeg?cs=srgb&dl=4k-wallpaper-christmas-lights-harry-potter-magic-1900185.jpg&fm=jpg');
            background-position: center;
        }

        table {
            border: 1px solid;
            border-collapse: collapse;
        }

        td,
        th {
            padding: 10px;
            border: 1px solid;
        }

        .curse {
            color: red;
        }

    </style>

    <script>
        $(document).ready(function () {
            $('#spell-info').empty();
        });

        function showSpell() {
            $('#spell-info').empty();
            $.ajax({
                type: "GET",
                url: "https://www.potterapi.com/v1/spells?key=$2a$10$LiNkiQtS86DQ8.NxC9G95.NN3.KkhNa917y/RZ6EjDILkjBaAJSLS",
                data: {},
                success: function(response) {
                    for (i = 0; i < response.length; i++) {
                        let types = response[i]['type'];
                        let spells = response[i]['spell'];
                        let effects = response[i]['effect'];

                        if (types == 'Curse') {
                            $('#spell-info').append(`<tr class="curse"><td>${types}</td><td>${spells}</td><td>${effects}</td></tr`)
                        } else {
                            $('#spell-info').append(`<tr><td>${types}</td><td>${spells}</td><td>${effects}</td></tr`)
                        }
//                      이런 방법도 가능
//                         if(spellType === 'Curse'){
//                             tempHtml = `<tr class="curse">\
//                                 <td>${spellType}</td>
//                                 <td>${spell}</td>
//                                 <td>${effect}</td>
//                               </tr>`;
//
//
//                         }else {
//                             tempHtml = `<tr>\
//                                 <td>${spellType}</td>
//                                 <td>${spell}</td>
//                                 <td>${effect}</td>
//                               </tr>`;
//                         }
//                         $('#spell-info').append(tempHtml);

                    }
                }
            })
        }
    </script>

</head>
<body>
<h1>Javascript + jQuery + Ajax 연습하기</h1>

<hr/>

<div class="question-box">
    <h2>해리 포터 시리즈에 나오는 마법 주문 API 이용하기</h2>
    <p>모든 주문 타입(type),주문(spell)과 효과(effect)를 화면에 보여주세요</p>
    <p>업데이트 버튼을 누를 때마다 데이터가 지웠다 새로 씌여져야 합니다.</p>
    <button onclick="showSpell()">업데이트</button>
    <table>
        <thead>
        <tr>
            <td>종류</td>
            <td>주문</td>
            <td>효과</td>
        </tr>
        </thead>
        <tbody id="spell-info">
        </tbody>
    </table>
</div>


</body>
</html>

 

문제2 : 사랑에 관한 책 확인하기

 

사용 할 API 요청 URL : https://openlibrary.org/subjects/love.json?published_in=1900-2000

API 정보: https://openlibrary.org/dev/docs/api/subjects

 

정답

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <title>20세기 사랑 주제의 책 보기</title>
    <!-- JQuery를 import 합니다 -->
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
    <style type="text/css">
        div.question-box {
            color: white;
            margin: 10px 0 20px 0;
            background-image: url("https://p0.pikist.com/photos/714/496/book-book-pages-novel-paperback-pitched-open-open-book-read-read-out.jpg");
            background-position: center;
        }

        table {
            border: 1px solid;
            border-collapse: collapse;
        }

        td,
        th {
            padding: 10px;
            border: 1px solid;
        }
    </style>

    <script>
        $(document).ready(function () {
            // 창을 새로고침할 때마다 실행할 함수를 여기에 입력합니다.
            $('#book-info').empty();
        });

        function showBookInfo() {
            $('#book-info').empty();
            $.ajax({
                type: "GET",
                url: "https://openlibrary.org/subjects/love.json?published_in=1900-2000",
                data: {},
                success: function(response) {
                    let works = response['works'];
                    for (i = 0; i < works.length; i++) {
                        let bookTitle = works[i]['title'];
                        let bootkAuthor = works[i]['authors'][0]['name'];
                        $('#book-info').append(`<tr><td>${bookTitle}</td><td>${bootkAuthor}</td></tr>`);
                    }
                }
            })
        }
    </script>

</head>
<body>
<h1>Javascript + jQuery + Ajax 연습하기</h1>

<hr/>

<div class="question-box">
    <h2>20세기 사랑 주제의 책 보기</h2>
    <p>모든 책의 제목(title),작가 이름(authors)을 화면에 보여주세요</p>
    <p>업데이트 버튼을 누를 때마다 데이터가 지웠다 새로 씌여져야 합니다.</p>
    <button onclick="showBookInfo()">업데이트</button>
    <table>
        <thead>
        <tr>
            <td>책 제목</td>
            <td>작가명</td>
        </tr>
        </thead>
        <tbody id="book-info">
        </tbody>
    </table>
</div>

</body>
</html>

 

 

 

문제3 : api 정보를 보고, 아래 조건의 출판으 책을 리스트업 하자.

 

사실상 2번과 동일한데, 좀 더 api쪽을 찾아보게 하는 예제.

- api 정보 : https://openlibrary.org/dev/docs/api/subject

- api 조건 : 21세기의 현재까지, 컴퓨터 주제

- 형식 : https://openlibrary.org/subjects/주제.json?published_in=연도

- 예시 : https://openlibrary.org/subjects/love.json?published_in=1900-2000

- 답 : https://openlibrary.org/subjects/computer.json?published_in=2000-2020

 

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <title>21세기 컴퓨터 주제 출판 책 보기</title>
    <!-- JQuery를 import 합니다 -->
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
    <style type="text/css">
        div.question-box {
            color: white;
            margin: 10px 0 20px 0;
            background-image: url("https://p1.pxfuel.com/preview/62/205/156/technology-hands-agreement-ok-screen-computer.jpg");
            background-position: center;
        }

        table {
            border: 1px solid;
            border-collapse: collapse;
        }

        td,
        th {
            padding: 10px;
            border: 1px solid;
        }
    </style>

    <script>
        $(document).ready(function () {
            $('#book-info').empty()// 창을 새로고침할 때마다 실행할 함수를 여기에 입력합니다.
        });

        function showBookInfo() {
            $('#book-info').empty()
            $.ajax({
                type: "GET",
                url: "https://openlibrary.org/subjects/computer.json?published_in=2000-2020",
                data: {},
                success: function(response) {
                    let works = response['works'];
                    for (i = 0; i < works.length; i++) {
                        let bookTitle = works[i]['title'];
                        let bootkAuthor = works[i]['authors'][0]['name'];
                        $('#book-info').append(`<tr><td>${bookTitle}</td><td>${bootkAuthor}</td></tr>`);
                    }
                }
            })
        }
    </script>

</head>
<body>
<h1>Javascript + jQuery + Ajax 연습하기</h1>

<hr/>

<div class="question-box">
    <h2>21세기 컴퓨터 주제 출판 책 보기</h2>
    <p>모든 책의 제목(title),작가 이름(authors)을 화면에 보여주세요</p>
    <p>업데이트 버튼을 누를 때마다 데이터가 지웠다 새로 씌여져야 합니다.</p>
    <!--  button 을 눌렀을 때 업데이트하려면 어떻게 해야할까요? -->
    <button onclick="showBookInfo()">업데이트</button>
    <table>
        <thead>
        <tr>
            <td>책 제목</td>
            <td>작가명</td>
        </tr>
        </thead>
        <!--  tbody 에 정보를 업데이트 하려면 어떻게 해야할까요? 힌트! 가리킨다(select)      -->
        <tbody id="book-info">
        </tbody>
    </table>
</div>


</body>
</html>

 

 

문제4 : 숫자 의미 api 사용하기

각 숫자를 받아 그 숫자의 의미를 api를 통해 출력한다.

 

api 주소 및 설명

http://numbersapi.com/#42

 

정답

<!Doctype html>
<html lang="ko">

<head>
    <meta charset="UTF-8">
    <title>Javascript + jQuery + Ajax 연습하기</title>
    <!-- JQuery를 import 합니다 -->
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>

    <style type="text/css">
        div.question-box {
            margin: 10px 0 20px 0;
        }
    </style>

    <script>
        function showNumMeaning() {
            if ($('#magic-num').val() == '') {
                alert("숫자를 입력하세요")
            } else {
                let inputVal = $('#magic-num').val();
                let targetAddress = "http://numbersapi.com/" + inputVal
                $.ajax({
                    type: "GET",
                    url: targetAddress,
                    data: {},
                    success: function (response) {
                        $("#meaning").text(response);
                    }
                })
            }
        }
    </script>

</head>

<body>
<h1>Javascript + jQuery + Ajax 연습하기</h1>

<hr/>

<div class="question-box">
    <h2>랜덤 숫자 의미부여 API를 이용하기</h2>
    <p>모든 숫자는 어떤 의미가 있습니다. 함께 살펴볼까요?</p>
    <p>아무것도 입력하지 않으면, alert을 띄워주세요.</p>
    <input type="number" id="magic-num">
    <button onclick="showNumMeaning()">이 숫자의 의미는?</button>
    <h5 id="meaning"></h5>
</div>
</body>

</html>

 

 

 

 

 

 

'SCC 9기' 카테고리의 다른 글

[SCC 9기] 4주차  (0) 2020.08.11
[SPC 9기] 3주차  (0) 2020.07.25
[SPC 9기] 1주차 - 프론트엔드  (0) 2020.07.16
[SCC 9기] 개발자를 위한 팁  (0) 2020.07.16
[SPC 9기] 0주차 - 사전준비/과제 및 이론수업  (0) 2020.06.11

기본 구조

웹사이트 기준으로 서버/클라이언트 구조에서는 다음과 같은 구조를 가진다.

 

<클라이언트>

브라우저

 

<서버>

Front-end (앞단에서 보이는 쪽, 즉 눈에 보이는 부분) : html , css

Back-end (뒤에서 일하는 쪽, 즉 눈에 보이지 않는 부분) : 데이터 관리, 즉 전송/저장/수집/가공 등을 수행

 

 

Front-end 단의 언어

 

HTML (hypertext markup language)

사이트의 뼈대를 만든다.

 

CSS

사이트를 실제로 꾸미는 역할 . 폰트크기, 색깔 , 기타등등

 

Bootstrap

꾸미는 것을 미리 만들어놓은 것. (framework) CSS를 예쁘게 꾸밀 수 있는것을 어느정도 미리 만들어 놨음. 가져다 쓰면 됨

 

Javascript

뼈대를 움직이게 한다. 브라우저가 유일하게 알아듣는 언어이다. (이렇게 움직여줘, 이렇게 그려줘 등)

 

 

HTML

 

* HTML 구조

- HEAD, BODY 로 구성됨.

 

HEAD : 웹페이지의 속성 정보(meta; 구글에서 페이지 정보를 가져감), CSS, JAVASCIPT 등

BODY : 웹페이지 내용

 

아래는 파이참에서 HTML를 만들면 기본적으로 주는 형식이다.

<!DOCTYPEhtml>
<html lang="en">

<head>
	<meta charset="UTF-8">  (글자형식)
	<title>Title</title>  (제목)
</head>

<body>
</body>

</html>

 

* 태그

- 기본 폼 : <태그이름 세부속성="속성값"> 내용 </태그이름>

- 태그는 여는 태그와 닫는태그로 이루어짐. <태그이름> </태그이름>

- 하나의 태그만 있는 것도 있음. 위에 <metacharset> 등

- 태그는 아주 많은데 개발자들이 모두 외우고 있지 않다. 필요할 때마다 구글에서 검색하면 된다.

 

 

기본 태그 정보

<meta>

상세히 알..필요가 있는지 모르겠다. 메타데이터?

 

<title></title>

웹페이지의 제목이다. 구글에서 검색할 때 관련이 있다.

 

<style></style>

head 태그에 들어가며, 꾸미는 것들은 다 style 태그에 들어가는게 일반적이다. CSS도 style 태그에 들어간다.

 

<div></div>

html에서 매우 중요한 역할을 한다. 뼈대가 되기 때문.

구역을 나눈다. 속성을 주지 않으면 어떻게 나뉘었는지 보이지 않는다.

구역을 나누는 이유는, 해당 구역의 폰트,글씨크기 등을 한번에 적용할 수 있다.

div를 잘 해놔야 나중에 수정할 때 편하다.

div는 중첩될 수 있다. 상위에서 하위 요소를 감싸고 있는 요소를 부모, 하위에 있는 요소를 자식이라고 한다.

빨간색 div는 부모이며, 녹색과 파란색 div는 자식이다.

여기서 빨간색 div 내용을 바꾸거나, 정렬을 바꾸는 등의 설정을 하면, 녹색, 파란색 div 안에있는 내용은 바뀌지 않고 그대로 유지된다.  즉 가장 안쪽에 있는 내용을 적용한다. 만약 파란색 div에 아무 설정을 하지 않으면, 빨간색 div의 설정을 따라간다.

* 예시 : 백그라운드 컬러 중첩

<head>
	<style>
        .bg-aqua {
            background-color: aqua;
        }
        .bg-red {
            background-color: red;
        }
        .bg-green {
            background-color: green;
        }
        .bg-gray {
            background-color: gray;
        }
        .bg-yellow {
            background-color: yellow;
        }
    </style>
</head>
<body>
<div class="bg-gray">

    <div class="bg-yellow">
        <div class="bg-aqua">
            <p>ID : <input type="text"/></p>
        </div>
        <div class="bg-green">
            <p>PW : <input type="password"/></p>
        </div>
    </div>

    <div class="bg-red">
            <button>로그인하기</button>
    </div>
</body>

여기서 버튼 부분에 bg-red 클래스를 없애면, 다음과 같이 나온다.

부모 div가 gray이므로 해당 색깔을 따라가는 것.

 

 

<p></p>

문단. 아랫줄이 한 줄 띄어진다.

 

<ul></ul>

리스트를 만들어 준다. 아래에 <li></li> 태그가 들어간다.

<ul>
    <li> bullet point!1</li>
    <li> bullet point!2</li>
</ul>

 

<h1></h1>

"제목"을 나타내는 태그. 단순히 크고 굵은 글씨가 아니다.

페이지마다 하나씩 꼭 쓰는것이 좋다. 구글 검색과 연관이 있다.

 

<h2></h2>

소제목을 나타낸다. 이 숫자에 따라 제목 크기가 작아진다.

 

<h3~6></h3~6>

다 각자의 역할이 있는데, 크게 사용성이 크진 않다.

 

<hr>

수평선을 그려준다. 단순히 <hr>만 넣으면 된다.

 

<span></span>

특정 글자를 지정할 때. 예를들어 꾸밀 때 쓴다. 정확하게 뭔지 모르겠다.

 

<img src="주소"/>

이미지 태그. 주소는 구글링 등으로 검색된 이미지의 주소를 넣거나 로컬 주소를 넣을 수 있다. 또한 아래 예시처럼 alt 라는 속성이 있는데, 이 속성은 해당 이미지가 깨지는 경우 이미지 대신 나오는 글귀이다.

 

<a href="주소">내용</a>

하이퍼링크를 만든다. 내용 부분을 클릭하면 해당 주소로 이동한다.

 

<input type="text"/>

글자를 입력받을 수 있는 공간을 만든다. type 부분에 text나 password 등의 형식을 넣을 수 있다.

 

<button></button>

버튼 태그. 누를 수 있는 버튼을 만든다.

 

<textarea>hello world</textarea>

글을 쓰는 칸을 만든다. 사이즈도 마우스로 조정이 가능하다.

 

 

html 실습예제

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>로그인 페이지</title>
</head>

<body>

<h1>로그인 페이지</h1>

<div>
    <p>ID : <input type="text"/></p>
    <p>PW : <input type="password"/></p>
</div>


<button>로그인하기</button>

</body>

</html>

- div 안에 ID, PW 두 라인이 묶여있다. 예를들어 ID, PW 줄을 가운데 정렬 하려면, DIV를 수정하면 된다.

- PW에서 *로 표시된 부분을 보이게 하려면 태그만 바꿔주면 쉽게 할 수 있다. password -> text로 바꾸는 식으로.

 

 

 

CSS

Cascading Style Sheet의 약자로, html에 디자인 요소를 (꾸미는) 언어인데, 꾸밀려면 선택을 해야 한다. 그래서 selector (선택자) 라는 요소를 사용한다. class의 이름은 자유롭게 지을 수 있고, 역할에 맞춰 잘 지어야 한다.

 

예를들어, mytitle이라는 이름표를 만들고, 그 이름표에 빨간색이라는 요소를 넣은 다음, html의 태그들에 mytitle 이름표를 붙이면, 해당 html 태그들에는 모두 빨간색이 적용되는 식이다.

 

 

선택자 사용하기

 

1. 선택자를 정의하고 : <head> 태그 안에, <style> 태그를 만들고, 그 안에 명시한다.  즉, 클래스를 만든다. 이러한 클래스를 여러개 만들려면 그대로 <style> 태그 안에 동일한 형식으로 쭉 만들면 된다.

        .red-font {
            color: blue;
        }

# 참고 : 자주 사용되는 선택자

선택자 안의 속성 (color 등)의 종류를 모두 알 필요는 없고, 필요할때마다 찾아서 해도 충분히 원하는 대로 만들 수 있다. 아래는 대표적인 예시이다.


배경관련

background-color

 

background-image

이미지의 경로를 넣는다. 해당 이미지는 백그라운드 이미지가 된다.

background-size

background-size: cover; 해당 이미지를 커버모드로 사용. 그림 사이즈에 따라 그림 모양을 맞춘다.

background-position

 

background-image: url("경로")

background-size: cover;

background-position: center;

이 3줄은 그냥 세트로 다닌다고 생각하면 됨. 경로만 바꿔서 사용한다.

속성


 

width

width: 300px; 그림의 너비

height

height: 300px; 그림의 높이

border-radius

border-radius: 10px; 개체의 모서리를 깎아준다.

border

개체의 테두리를 만든다. 아래 속성의 순서는 상관없다.
border 두께 타입 색깔;  
border 2px solid black; 

글자관련

font-size

 

font-weight

 

font-family

 

color

폰트의 색깔을 설정한다.

text-align

text-align:center; 글자를 가운데 정렬한다.

간격

margin

요소의 외부 여벽 (요소와 화면과의 여백). 즉 그림의 외부 여백. 해당 요소 자체가 적용받는 여백. 

auto로 설정하면, 화면에 맞게 조정된다. 너비는 auto가 되지 않는다.

margin:10px auto; 위에는 10픽셀만, 양옆아래는 자동으로

margin : 해당 개체 밖의 여백, 위 오른쪽 아래 왼쪽;

margin: 20px 20px 20px 20px;

어떤 요소를 가운데로 가져오려면 width, margin을 활용한다. (width로 사이즈를 만들고, 그 사이즈를 margin으로 가운데로 옮기는 것)

padding

요소 내부의 여백. 즉 그림의 내부
경계선과 내부 요소의 간격은 padding으로 조절한다.
padding은 auto가 없다. 


padding: 해당 개체 안의 여백. 값 1개만 쓰면 위 오른쪽 아래 왼쪽 모두 적용된다. 

padding: 20px 20px 20px 20px;  (위 오른쪽 아래 왼쪽)
상하좌우 각각 따로하려면 다음 링크를 참고한다. aboooks.tistory.com/81 (항상 대칭 형태로 적용된다고 

padding-top : 개체의 내부 위쪽 여백
padding-top: 40px; 해당 요소 내부에서, 윗부분을 40px로 함.

참고 : www.walterz.net/2017/08/18/%EC%9E%90%EC%A3%BC-%EC%82%AC%EC%9A%A9%EB%90%98%EB%8A%94-css-%EC%86%8D%EC%84%B1/

 

[CSS] 자주 사용되는 스타일 시트 속성 정리

웹기술이 발전하면서 동적이고 화려한 웹페이지가 등장하게 되었습니다. 특히 웹기술 중에서 CSS는 자바스크립트와 더불어 웹페이지의 각종 동적인 속성을 지정할 수 있는 간단한 스크립트입니

www.walterz.net

 

2. 태그에 선택자를 적용한다 : <body> 태그 안에 원하는 태그에 적용한다.

<h1 class="red-font">로그인 페이지</h1>

아래 예시에서는 .red-font 라는 클래스 선택자를 만들었고, h1과 div 태그에 해당 클래스 선택자를 적용했다.

 

 

# 참고 : 용어의 정의

클래스(class) : CSS에서 .xx { } 의 형태를 클래스라고 한다. (클래스 만들 때 이름 앞에 .을 꼭 붙여야 한다!)

선택자(selector) : html 태그를 선택해 꾸미기 위한 방법. 이러한 클래스 앞에 선택자 이름이 붙고, 그 이름으로 태그를 선택해서 꾸민다.

<!DOCTYPE html>
<html lang="ko">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>로그인</title>
    <style>
        .red-font {
            color: blue;
        }
    </style>
</head>

<body>
    <h1 class="red-font">로그인 페이지</h1>
    <div class="red-font">
        <p>
            ID: <input type="text" />
        </p>
        <p>
            PW: <input type="password" />
        </p>
    </div>
    <button>로그인하기</button>
</body>

</html>

만약 아래처럼 color를 red로 바꾸면, h1과 div 태그 모두 붉은색으로 바뀐 것을 알 수 있다.

    <style>
        .red-font {
            color: red;
        }
    </style>

# 참고

여기서 색깔은 red, blue, green 등 여러가지 정의된 색깔명이 있다. 찾아서 쓰면 된다. 파이참에서는 아래와 같이 찾을 수 있다.

 

 

여러가지 선택자

가독성 있는 코드를 위해 현업에서는 규칙/상황에 따라 다양한 선택자를 사용하며, 여기서는 대표적으로 중요한 것만 써본다.

 

1. 전체 선택자 (*)

HTML 페이지 내부의 모든 태그를 선택

* { 
   margin: 0;
   text-decoration: none;
}

 

2. 태그 선택자

HTML 페이지 내부에서, 해당 선택자에서 명시한 태그를 모두 선택 (예시에서는 p태그) 

p { 
  background: yellowgreen;
  color: darkgreen;
}

<!-- HTML -->  
<p>태그 선택자(Type Selector)</p>  

 

3. 클래스 선택자 

class1라는 클래스를 가진 모든 태그에 적용된다.

.class1 { 
  background: yellowgreen; 
  color: darkgreen; 
}

<p class="class1">케이스1</p>
<p class="class2">케이스2</p>

 

4. 자식 선택자

div 태그 중 class2 라는 클래스를 가진 태그에 적용된다.

div.class2 {
  background: darkgreen;
  color: yellowgreen;
}

<div class="class2">케이스3</div>

 

 

 

 

한 태그에 선택자 여러개 사용하기

여러개의 선택자를 만들었다면, 태그에 단순히 띄어쓰기 후 추가하면 된다.

    <style>
        .red-font {
            color: blue;
        }
        .big-font {
            font-size: 50px;
        }
    </style>
    
	<h1 class="red-font big-font">로그인 페이지</h1>

 

 

 

예시 : 여러가지 css 사용해보기

<!DOCTYPE html>
<html lang="ko">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>로그인</title>
    <style>
        .login-title {
            color: white;
            width: 300px;
            height: 200px;
            background-image: url('https://www.ancient-origins.net/sites/default/files/field/image/Agesilaus-II-cover.jpg');
            background-position: center;
            background-size: cover;
            
            border-radius: 10px;
            text-align: center;
            padding-top: 40px;
        }
    </style>
</head>

<body>
    <div class="login-title">
        <h1>로그인 페이지</h1>
        <h5>아이디, 비밀번호를 입력해주세요</h5>
    </div>
    <div>
        <p>
            ID: <input type="text" />
        </p>
        <p>
            PW: <input type="password" />
        </p>
    </div>
    <button>로그인하기</button>
</body>

</html>

 

예시 : 같은 내용인데, 전체 내용을 옮겨보자.

- 위 예시에 새로운 클래스를 만들고 wrap이라는 선택자 이름을 주었다.

- body에는 div를 하나 더 만들어 내용 전체를 감싸고, wrap 클래스를 적용했다.

- wrap에 width를 300 넣은 이유는, 해당 div 부분 자체의 사이즈 300을 만들고 (이 안에 그림, 글자가 있다) 이 width 300짜리 div를 가운데 정렬하는 방식을 사용하기 위함임.

- margin과 padding을 정확히 이해하려면 백그라운드 컬러를 적용하면 이해가 쉽다.

 

어떤 내용모음의 위치를 변경하려면, div로 묶은다음, 그 div에 margin이나 padding을 주면 된자.

 

백그라운드 컬러를 주고 div로 나누면 이렇게 된다.

이걸 사이즈를 변경한다. width로. 그럼 이렇게 사이즈가 줄은다.

이걸 margin 으로 가운데 이동하면 되며, 가운데는 auto 이다.

 

 

 

<!DOCTYPE html>
<html lang="ko">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>로그인페이지</title>
    <style>
        .login-title {
            color: white;
            width: 300px;
            height: 200px;
            background-image: url('https://www.ancient-origins.net/sites/default/files/field/image/Agesilaus-II-cover.jpg');
            background-position: center;
            background-size: cover;

            border-radius: 10px;
            text-align: center;
            padding-top: 40px;
        }
        .wrap {
            margin: 10px auto;
            width: 300px;
        }


    </style>
</head>

<body>
    <div class="wrap">
        <div class="login-title">
            <h1>로그인 페이지</h1>
            <h5>아이디, 비밀번호를 입력해주세요</h5>
        </div>
        <div>
            <p>
                ID: <input type="text" />
            </p>
            <p>
                PW: <input type="password" />
            </p>
        </div>
        <button>로그인하기</button>
    </div>
</body>
</body>

</html>

양옆은 가운데, 위는 10픽셀이 적용되었다.

 

 

 

구글 웹폰트 적용하기

-  구글 웹폰트는 저작권 상관없이 자유롭게 사용가능하다.

 

1. 구글 웹폰트 사이트에 들어가서 마음에 드는 폰트 선택

https://fonts.google.com/?subset=korean

2. 원하는 스타일을 선택한다.

 

3. 오른쪽과 같은 화면이 나오면, Embed를 선택한다.

4. 홈페이지에 삽입하기

Embed 부분 아래에 link를 복사하여, 홈페이지의 <head></head> 사이에 삽입한다.

<head>
	<link href="https://fonts.googleapis.com/css2?family=Stylish&display=swap" rel="stylesheet">
</head>

또한, CSS가 들어가는 <style> 부분에 원하는 범위만큼 CSS rules를 넣는다. (아래 예시에선 전체 태그에 대해 적용했다)

* {
    font-family: 'Stylish', sans-serif;
}

5. 결과

다음과 같이 모든 글자가 적용되었다.

 

CSS 파일을 따로 만들기

예시에서는 html 파일에 CSS 내용을 모두 포함했으나, 사이트가 커지고 내용이 많아지면 복잡해지므로, CSS 부분만 따로 파일로 만들어서 연결할 수 있다.

 

 

 

1. CSS 내용만 뽑아 파일명.css 로 파일을 생성한다. (<style> 태그도 없다)

파일명 : design.css

.login-title {
    color: white;
    width: 300px;
    height: 200px;
    background-image: url('https://www.ancient-origins.net/sites/default/files/field/image/Agesilaus-II-cover.jpg');
    background-position: center;
    background-size: cover;

    border-radius: 10px;
    text-align: center;
    padding-top: 40px;
}

.wrap {
    margin: 10px auto;
    width: 300px;
    background-color: green;
}

* {
    font-family: 'Nanum Myeongjo', serif;
}

2. 만든 CSS 파일을 적용하기

- 해당 CSS 파일을 적용할 html 파일의 <head> 안에 다음 구문을 삽입한다.

- href 부분에 CSS파일의 위치를 넣는다. 만약 해당 html 파일과 같은 경로게 있다면 CSS파일의 파일명만 명시하면 된다.

<link rel="stylesheet" type="text/css" href="design.css"/>

- 참고로, 폰트태그는 따로 CSS파일에 넣을 수 없으므로 html 본파일의 <head>에 삽입해야 한다.

 

 

 

CSS 연습하기

CSS 선택자 연습하기 (난이도:별1)

https://flukeout.github.io/

 

CSS 컬러링 북

CSS 파일의 속성값을 수정해 색깔을 칠해보자. (난이도:별1)

https://codepen.io/Lubna/pen/zYYzOxN

https://codepen.io/Lubna/pen/dyoqzwP

https://lubna.dev/articles/colouring-with-code/

 

CSS의 속성 중 하나인 flexbox를 게임으로 배워보자 (난이도:별3)

https://flexboxfroggy.com/#ko

 

 

 

 

 

 

 

부트스트랩

css 템플릿을 모아놓은 일종의 디자인 키트. 미리 만들어진 디자인들을 가져다 쓸 수 있다. 이런 개념을 더 정확히 말하면, Front-end framework 라고 한다. 트위터에서 만든 것이다. 한마디로, 다른 사람이 미리 작성한 CSS를 내 HTML파일에서 적용하게 된다. 이런 차원에서 부트스트랩 적용은 CSS 파일을 따로 분리해놓은 것과 원리가 동일하다. 다만 CSS파일을 부트스트랩에서 받는다는 것이다.

 

개발자들이 활용하는데 매우 좋다. 부트스트랩 외에도 다른 종류가 많이 있다. CSS를 다룰 줄 아는 것과 예쁘게 만드는 것은 다르다. 개발자들도 미리 완성된 부트스트랩을 가져다 쓰는 경우가 많다. 다음 절차대로 부트스트랩을 사용해보자.

 

 

1. 부트스트랩 코드를 <head>에 적용한다.

이 내용은 부트스트랩 공식내용 https://getbootstrap.com/docs/4.0/getting-started/introduction/ 에서 starter templet을 참고했으며, javascript 내용 부분이 약간 다르다. 이건 스파르타에서 준 것이다.

<!doctype html>
<html lang="ko">

<head>
    <!-- Required meta tags -->
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

    <!-- Bootstrap CSS 부트스트랩에서 제공하는 CSS를 import 한 것-->
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css"
    	  integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">

    <!-- Optional JavaScript 필요할 때 쓸 javascript를 import 한 것-->
    <!-- jQuery first, then Popper.js, then Bootstrap JS -->
	<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js"
        		integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q"
       			crossorigin="anonymous"></script>
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js"
        		integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl"
        		crossorigin="anonymous"></script>

    <title>스파르타코딩클럽 | 부트스트랩 연습하기</title>
</head>



<body>
    <h1>이걸로 시작해보죠!</h1>
</body>

</html>

여기서 <link> 함수를 보면 https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css 부분이 있다. 이 CSS파일이 부트스트랩에서 제공하는 전체 CSS 내용들이다. 한번 들어가보자. 들어갈 땐 min을 빼고 들어가보자.

여기 보면 뭔가 엄청 많은데, 이게 다 CSS들이다. 이걸 참조해서 내 사이트에 적용하는 것이다. 실무에서 부트스트랩을 사용하는 경우, 성능때문에 부트스트랩 전체를 로딩하지 않고 필요한부분만 빼오거나, 직접 만드는 css를 쓰는 경우가 많다.

 

 

 

 

2. 부트스트랩 사이트에서 원하는 CSS를 찾는다.

https://getbootstrap.com/docs/4.0/components 에 접속하면, 다음과 같은 사이트가 나온다. 오른쪽에 보면 Components라는 항목이 있고, 아래에 여러가지가 있다. 얼럿, 뱃지, 버튼, 카드, 폼 등등.. 이런 요소들이 모두 HTML 홈페이지에서 사용하는 것이다.

 

# 참고 : 부트스트랩에서 개체 가져올 때

부트스트랩에서 한 개체를 받고, 개체를 수정하는 경우, 한 라인만 삭제하지 않을 수도 있음. 잘 보고 삭제하자. 새로운거 넣을때도 잘 보고 넣어야 함.

 

 

 

3. 찾은 CSS를 넣어서 적용해본다.

예시로, Buttons에 들어가보자. 다음과 같은 화면이 나오며, 여러가지 버튼들을 사용할 수 있다. 원하는 것을 복사해보자.

여기서는 파란색 Primary 버튼을 복사했다. Primary 부분을 원하는 대로 글자를 넣고 아래 스크립트를 <BODY> 태그 안에 넣어보자. 즉, 아래처럼 된다.

<!doctype html>
<html lang="ko">

<head>
    <!-- Required meta tags -->
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

    <!-- Bootstrap CSS 부트스트랩에서 제공하는 CSS를 import 한 것-->
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css"
    	  integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">

    <!-- Optional JavaScript 필요할 때 쓸 javascript를 import 한 것-->
    <!-- jQuery first, then Popper.js, then Bootstrap JS -->
	<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js"
        		integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q"
       			crossorigin="anonymous"></script>
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js"
        		integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl"
        		crossorigin="anonymous"></script>

    <title>스파르타코딩클럽 | 부트스트랩 연습하기</title>
</head>



<body>

    <h1>이걸로 시작해보죠!</h1>
    
	<button type="button" class="btn btn-primary">안녕하세요</button>

</body>

</html>

 

이 코드를 실행하면 다음과 같이 나오게 된다.

<button type="button" class="btn btn-primary">안녕하세요</button>

이 버튼은 btn, btn-primary 라는 class가 적용된 것이다. 이 class는 위의 부트스트랩 링크에서 가져온 것이고.

 

다른 예로, 카드는 이런 모양이다.

코드는 다음과 같다. 이 코드가 위의 카드를 만드는 것이다. 잘 보면, div로 시작해서 div로 끝난다. 하나의 객체로 이해할 수 있다.

<div class="card" style="width: 18rem;">
  <img class="card-img-top" src="..." alt="Card image cap">
  <div class="card-body">
    <h5 class="card-title">Card title</h5>
    <p class="card-text">Some quick example text to build on the card title and make up the bulk of the card's content.</p>
    <a href="#" class="btn btn-primary">Go somewhere</a>
  </div>
</div>

 

 

부트스트랩을 사용하여 html, CSS를 활용한 페이지 예시 만들기

<!doctype html>
<html lang="ko">

<head>
    <!-- Required meta tags -->
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

    <!-- Bootstrap CSS -->
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css"
          integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">

    <!-- JS -->
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js"
            integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q"
            crossorigin="anonymous"></script>

    <!-- 구글폰트 -->
    <link href="https://fonts.googleapis.com/css?family=Stylish&display=swap" rel="stylesheet">


    <title>스파르타코딩클럽 | 부트스트랩으로 나홀로 메모장 만들기</title>

    <!-- style -->
    <style type="text/css">
        * {
            font-family: 'Stylish', sans-serif;
        }

        .wrap {
            width: 900px;
            margin: auto;
        }

        .comment {
            color: blue;
            font-weight: bold;
        }

        .post-box {
            width: 500px;
            margin: 20px auto;
            padding: 50px;
            border: black solid;
            border-radius: 5px;
        }
    </style>

</head>

<body>
<div class="wrap">
    <div class="jumbotron">
        <h1 class="display-4">나홀로 링크 메모장!</h1>
        <p class="lead">중요한 링크를 저장해두고, 나중에 볼 수 있는 공간입니다</p>
        <hr class="my-4">
        <p class="lead">
            <a class="btn btn-primary btn-lg" href="#" role="button">포스팅박스 열기</a>
        </p>
    </div>
    <div class="form-post post-box">
        <div>
            <div class="form-group">
                <label for="exampleFormControlInput1">아티클 URL</label>
                <input class="form-control" placeholder="">
            </div>
            <div class="form-group">
                <label for="exampleFormControlTextarea1">간단 코멘트</label>
                <textarea class="form-control" rows="2"></textarea>
            </div>
            <button type="button" class="btn btn-primary">기사저장</button>
        </div>
    </div>
    <div class="card-columns">
        <div class="card">
            <img class="card-img-top"
                 src="https://www.eurail.com/content/dam/images/eurail/Italy%20OCP%20Promo%20Block.adaptive.767.1535627244182.jpg"
                 alt="Card image cap">
            <div class="card-body">
                <a href="#" class="card-title">여기 기사 제목이 들어가죠</a>
                <p class="card-text">기사의 요약 내용이 들어갑니다. 동해물과 백두산이 마르고 닳도록 하느님이 보우하사 우리나라만세 무궁화 삼천리 화려강산...</p>
                <p class="card-text comment">여기에 코멘트가 들어갑니다.</p>
            </div>
        </div>
        <div class="card">
            <img class="card-img-top"
                 src="https://www.eurail.com/content/dam/images/eurail/Italy%20OCP%20Promo%20Block.adaptive.767.1535627244182.jpg"
                 alt="Card image cap">
            <div class="card-body">
                <a href="#" class="card-title">여기 기사 제목이 들어가죠</a>
                <p class="card-text">기사의 요약 내용이 들어갑니다. 동해물과 백두산이 마르고 닳도록 하느님이 보우하사 우리나라만세 무궁화 삼천리 화려강산...</p>
                <p class="card-text comment">여기에 코멘트가 들어갑니다.</p>
            </div>
        </div>
        <div class="card">
            <img class="card-img-top"
                 src="https://www.eurail.com/content/dam/images/eurail/Italy%20OCP%20Promo%20Block.adaptive.767.1535627244182.jpg"
                 alt="Card image cap">
            <div class="card-body">
                <a href="#" class="card-title">여기 기사 제목이 들어가죠</a>
                <p class="card-text">기사의 요약 내용이 들어갑니다. 동해물과 백두산이 마르고 닳도록 하느님이 보우하사 우리나라만세 무궁화 삼천리 화려강산...</p>
                <p class="card-text comment">여기에 코멘트가 들어갑니다.</p>
            </div>
        </div>
        <div class="card">
            <img class="card-img-top"
                 src="https://www.eurail.com/content/dam/images/eurail/Italy%20OCP%20Promo%20Block.adaptive.767.1535627244182.jpg"
                 alt="Card image cap">
            <div class="card-body">
                <a href="#" class="card-title">여기 기사 제목이 들어가죠</a>
                <p class="card-text">기사의 요약 내용이 들어갑니다. 동해물과 백두산이 마르고 닳도록 하느님이 보우하사 우리나라만세 무궁화 삼천리 화려강산...</p>
                <p class="card-text comment">여기에 코멘트가 들어갑니다.</p>
            </div>
        </div>
        <div class="card">
            <img class="card-img-top"
                 src="https://www.eurail.com/content/dam/images/eurail/Italy%20OCP%20Promo%20Block.adaptive.767.1535627244182.jpg"
                 alt="Card image cap">
            <div class="card-body">
                <a href="#" class="card-title">여기 기사 제목이 들어가죠</a>
                <p class="card-text">기사의 요약 내용이 들어갑니다. 동해물과 백두산이 마르고 닳도록 하느님이 보우하사 우리나라만세 무궁화 삼천리 화려강산...</p>
                <p class="card-text comment">여기에 코멘트가 들어갑니다.</p>
            </div>
        </div>
        <div class="card">
            <img class="card-img-top"
                 src="https://www.eurail.com/content/dam/images/eurail/Italy%20OCP%20Promo%20Block.adaptive.767.1535627244182.jpg"
                 alt="Card image cap">
            <div class="card-body">
                <a href="#" class="card-title">여기 기사 제목이 들어가죠</a>
                <p class="card-text">기사의 요약 내용이 들어갑니다. 동해물과 백두산이 마르고 닳도록 하느님이 보우하사 우리나라만세 무궁화 삼천리 화려강산...</p>
                <p class="card-text comment">여기에 코멘트가 들어갑니다.</p>
            </div>
        </div>
    </div>
</div>
</body>

</html>

참고로, 부트스트랩에서 가져올 때 <form> 태그는 안에 버튼이 있으면 자동으로 새로고침이 되므로, 이 기능은 쓰지 않으니 <div> 태그로 바꿔준다.

 

 

 

자바스크립트

자바스크립트는 html 화면에 동적인 움직임을 줄 때 사용한다. 버튼을 클릭했을 때 색이 바뀌거나, 경고창이 뜨거나, div박스를 숨기는 등 여러가지 역할을 수행할 수 있다.

 

자바스크립트는 프로그래밍 언어로, 웹 브라우저가 해석할 수 있는 언어이다. 짧게 js 라고 표시하기도 한다. 왜 브라우저에서 자바스크립트만 사용하냐면 (파이썬이나 자바 등의 언어를 쓰면 안되나), 역사적이유와 이미 만들어진 표준이기 때문 모든 브라우저는 기본적으로 자바스크립트를 알아듣게 설계되어 있음.

 

또한 모든 웹서버는 클라이언트가 서버에 요청하면 서버는 HTML+CSS+Javascript를 응답(response) 데이터로 주게 되어있다.

 

 

* 기본 사용 형식

 

1. 에 자바스크립트 함수 삽입하기

<script>
function sayHello(){
	alert('안녕!');
}
</script>

 

2. 자바스크립트 함수 적용하기

## 원래 버튼
<button type="button" class="btn btn-primary">안녕하세요</button>

## 버튼에 아래 함수를 적용한다.
onclick="sayHello()"

## 다음과 같이 된다.
<button onclick="sayHello()" type="button" class="btn btn-primary">안녕하세요</button>

 

* 예시코드

이 예시는 안녕하세요 버튼을 누르면 안녕!이라는 메시지를 출력한다.

<!doctype html>
<html lang="ko">

<head>

    <!-- Required meta tags -->
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

    <!-- Bootstrap CSS -->
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css"
          integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">

    <!--Javascript-->
    <script>
        function sayHello() {
            alert('안녕!');
        }
    </script>

    <title>자바스크립트 테스트</title>

</head>

<body>

<button onclick="sayHello()" type="button" class="btn btn-primary">안녕하세요</button>

</body>

</html>

# 참고 : JS 코드를 적용하는 2가지 방법

- CSS와 마찬가지로 JS파일을 따로 분리하고 HTML 코드에 추가해서 사용

- </body> 바로 직전에 <script></script> 코드를 사용

여기 예시에서는 head에 JS 코드를 넣어도 실행하는데 큰 문제가 없어 가독성을 위해 <head> 안에 넣음.

 

 

* 프로그래밍의 기본 요소.  일단 이것만 알면 반은 들어간다.

- 변수

- 자료형

- 함수

- 반복문

- 가정문

 

 

# 참고사항 : 자바스크립트 문법

- 자바스크립트에서 = 은 같다가 아니고 "오른쪽에 있는 것을 왼쪽에 넣어라" 라는 뜻임. (대부분의 프로그래밍언어가 비슷)

- 세미콜론은 한 줄의 마무리이다.

 

 

변수

 

* 기본형식

- 변수는 값을 담는 상자이다.

- 자바스크립트에서 변수 선언은 let 을 사용한다.

let 변수명 = 넣을내용;

- 한 번 변수를 선언했으면, 다시 선언할 필요 없고 계속 변수를 사용하면 된다.

- 숫자는 그냥 넣으면 되고, 문자는 따옴표 또는 쌍따옴표를 포함해야 한다.

- 변수명을 입력하면, 그 변수에 들어간 값이 출력된다.

- 예시

 

* 연산

- 사칙연산이 기본적으로 가능하며, 문자열을 더할수도 있다.

 

부울형식

참 거짓, 즉 true/false를 나타내는 자료형이다. 주로 조건문과 함께 쓰인다. 비교연산자를 사용하면, 비교를 해서 true/false를 출력한다.

비교연산자는 >, <, ==(같다), !=(다르다) 등이 있다. 참고로 !는 not 이다. 다른곳에서도 사용됨.

 

 

 

 

리스트

자료(Data)를 담는 형태(type) 중 하나. 가장 많이 쓰이는 자료형 중 하나임. 리스트는 순서가 있고 순서대로 값을 가지는 형태이다. 각 값들은 순서를 가지며 순서는 앞부터 0으로 시작한다. 즉 0,1,2,3,4 .. 이렇게 시작한다.

 

 

* 리스트 선언

[ ] 를 사용해서 선언한다. 

let sampleList = [];   // 빈 리스트 만들기
let mltiList = [1, 2, 'hello', 4];

 

* 리스트 특정 내용 출력하기

* 리스트에 값 추가하기

push 함수를 사용하며, 맨 뒤부터 순서대로 값이 들어간다.

참고로 빼는 건 pop() 이다. 항상 맨 끝에 있는 값을 빼므로 특정 값을 명시할 필요는 없다.

 

 

* 리스트의 길이 구하기

반복문에서 유용하게 쓰인다. length 함수를 사용한다.

 

* 리스트에 리스트 넣기

 

1. 리스트를 2개 선언한다.

2. push() 함수를 이용하여 리스트에 리스트를 넣는다.

numberList 리스트에 charList 라는, 6개의 어레이를 가진 하나의 개체가 맨 뒷부분에 들어갔다.

 

3. 리스트에 들어간 리스트에 접근하기

다음과 같이 numberList[6]의 [0]번째, [4]번째 등을 접근할 수 있다.

 

딕셔너

"키-밸류" 형태의 값 쌍을 넣는다. 예를들어 "단어-뜻" 이런 형태로 들어가게 된다.

 

* 선언하기

중괄호를 사용하여 선언한다. 딕셔너리를 만들고 확인할때는 '키'를 선택하면 그에 맞는 '밸류'가 나온다. 밸류를 명시했을 때는 맞는 키를 확인할 수 없다. 즉 '키'가 메인이다.

let sampleDict = {};   // 빈 딕셔너리 만들기
let sampleDict = {'name' : 'james', 'age' : 44}

 

* 키의 밸류값 바꾸기

변수처럼, 해당 "키값 = 밸류값" 형태로 넣으면 된다.

 

* 딕셔너리에 값 추가하기

밸류값 바꾸는 형식과 동일한데, 키도 새로운 키를 넣으면 된다.

 

 

* 리스트와 딕셔너리를 조합하기

 

1. 딕셔너리에 -> 리스트 추가

형식은 동일하며, 딕셔너리에 어레이를 추가할 수 있다. 다음과 같이 실행된다.

 

2. 리스트에 -> 딕셔너리 추가

딕셔너리는 순서가 없으므로, 리스트와 딕셔너리를 조합해서 순서가 있는 딕셔너리를 만들 수 있다. 리스트에 딕셔너리가 조합되면 리스트에 push를 해서 추가할 수 있다. 이렇게 자료형을 연계해서 쓰는 이유는 관리의 용이성, 가독성, 코드효율성 등을 위해서이다.

 

예를들어, 가게에서 대기표를 작성하기 위해 이름과 전화번호를 기록하는데,  변수만 쓰면 다음과 같이 복잡하다.

let customer1Name = '김스파';
let customer1Phone = '010-1234-1234';
let customer2Name = '박르탄';
let customer2Phone = '010-4321-4321';

 

하지만 딕셔너리를 사용하면, 다음과 같이 고객별로 정보를 묶을 수 있다. 물론 값은 최대 2개의 종류 (이름,전화번호)이지만.

let customer1 = {'name': '김스파', 'phone': '010-1234-1234'};
let customer2 = {'name': '박르탄', 'phone': '010-4321-4321'};

 

여기다가 리스트까지 포함하면, 매우 깔끔하고 간단하다. 거기에 추가를 한다면 간단하게 push로 할 수 있다.

let customers = [
    {'name': '김스파', 'phone': '010-1234-1234'},
    {'name': '박르탄', 'phone': '010-4321-4321'}
]

 

 

다음 예시는 wizards 라는 리스트에 0번에는 이름:해리포터, 나이:40 이라는 딕셔너리를 넣고, 1번에는 이름:론위즐리, 나이:40 이라는 딕셔너리를 넣은 것이다.

리스트에 새로운 딕셔너리 값을 추가하려면, 다음과 같이 딕셔너리를 선언하고, 선언한 딕셔너리를 해당 리스트에 push() 함수로 입력한다.

 

 

 

 

함수

사칙연산 외에도 여러가지 제공하는 함수들이 있다. 또 직접 만들수도 있다. 왠지 이건 있을 것 같은? 것들, 예를들어 대문자의 갯수만 센다던지.. 그런것들은 구글링하면 있을 수 있으니 굳이 직접 만들 필요는 없다.

 

또한 미리 구현된 함수는 아래처럼 구문 중 . 을 넣어서 목록을 볼 수 있다. 그외에도 더 있는 듯 함.

 

* 예시 : 나눗셈의 나머지를 구하는 연산자

% 연산자. 나머지를 출력한다. 

나머지를 구하는 기능은, 예를들어 짝수면 액션 A를 하고, 홀수면 액션 B를 해라 이런 식의 조건에서 많이 사용된다. 2로 나눠 나머지가 1인지 0인지로 확인할 수 있다. 즉 num % 2 == 0 의 결과가 참이될려면 num이 짝수여야만 한다. 이런식으로 사용된다.

 

아니면, 나머지가 0이면 a로, 나머지가 1이면 b로 나머지가 2이면 c로 이런식으로 분배하는 코딩에서 사용할 수 있다.

 

 

* 예시 : 모든 알파벳을 대문자로 바꾸는 함수

toUpperCase() 함수는 모든 알파벳을 대문자로 바꿔준다.

 

 

* 예시 : 문자열을 나누는 함수

split() 함수는 문자열에서 특정 문자를 기준으로 둘로 나눈다. 예를들어, testmail@gmail.com 에서, @을 기준으로 나누면 testmail , gmail.com 두개가 생기게 된다. 이렇게 생긴 결과는 순서대로 리스트로써 변수에 들어간다. @는 삭제된다.

만약 여기서 리스트를 만들지 않고 naver.com만 딱 찍어내고 싶다면, let splitResult2 = mailAdd.split('@')[1]; 로 하면 된다.

 

만약 딱 naver까지만 찍어내고 싶다면? let splitResult3 = mailAdd.split('@')[1].split('.')[1]; 이렇게 하면 된다.

이런식으로 만들면 이메일에서 특정 문자만 추출하는 함수 등을 직접 만들어 볼 수도 있다.

 

 

* 예시 : 문자열을 합치는 함수

join() 함수는 특정 문자로 합치는 함수이다. 다음과 같이 특정 문자, 여기서는 '-'로 문자들을 합쳤다. '-' 문자가 새로 생기게 된다.

 

 

함수 만들기

함수는 수학의 함수가 아닌, 정해진 동작을 수행하는 모듈 이라고 생각하면 좋다. 이렇게 함수를 만들어 쓰는 대표적인 이유 중 하나는 "재사용" 때문이다.

 

* 함수를 만들고 사용하는 기본 틀

// 만들기
function 함수이름(필요한 인수들) {
	내릴 명령어들을 순차적으로 작성
}

// 사용하기
함수이름(필요한 변수들);

* 예시 : 두 숫자를 입력받으면 곱한 결과를 돌려주는 함수

function multiply1(num1, num2) {
	console.log('num1: ', num1, ', num2: ', num2);
    return num1 * num2;
}

함수에 따라 return 항목이 있을수도 있고 없을수도 있다. return이 없으면 함수 내부에서 모든게 끝나며 결과로써는 아무것도 보이지 않음. 또한 인수를 받지 않는 경우도 있음. 인수는 여러개를 받는다면, 순서대로 넣어야 한다. 따라서 사용자가 순서를 알 수 있도록 잘 명시해야 한다.

 

 

 

 

 

 

 

 

 

 

 

* 숙제와 답

아래 레이아웃대로 만들기. 주문하기 버튼을 눌렀을 때 "주문이 완료되었습니다" 알림이 떠야 한다.

숙제는 내가 한 파일 참고

 

답 예시

<!DOCTYPE html>
<html lang="ko">
    <head>
        <!-- Webpage Title -->
        <title>나홀로 쇼핑몰</title>

        <!-- Required meta tags -->
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

        <!-- Bootstrap CSS -->
        <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css"
              integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm"
              crossorigin="anonymous">

        <!-- JS -->
        <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
        <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js"
                integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q"
                crossorigin="anonymous"></script>

        <!-- 구글폰트 -->
        <link href="https://fonts.googleapis.com/css?family=Stylish&display=swap" rel="stylesheet">

        <style type="text/css">
            * {
                font-family: 'Stylish', sans-serif;
            }

            .wrap {
                width: 500px;
                margin: auto;
            }

            .img {
                background-image: url('https://www.conscious-skincare.com/wp-content/uploads/2016/02/glc-candle-lit-with-new-gift-box.jpg');
                background-size: cover;
                background-position: center;
                width: 500px;
                height: 300px;
            }

            .info {
                margin-top: 20px;
                margin-bottom: 20px;
            }

            h1, h5 {
                display: inline;
            }

            .order {
                text-align: center;
            }

            .orders {
                margin-top: 100px;
            }
        </style>

        <script>
            function order() {
                // 여기에 코드를 작성하세요.
            }

        </script>
    </head>
    <body>
        <div class="wrap">
            <div class="img"></div>
            <div class="info">
                <h1>양초를 팝니다</h1>
                <h5>가격: 3,000원/개</h5>
                <p>이 양초는 사실 특별한 힘을 가지고 있어요. 양초를 켜고 소원을 빌면 짜자잔 뭐든지 이뤄지게 된답니다. 하나 사가세요! 대나무 향이 아주 좋아요</p>
                <div class="input-group mb-3">
                    <div class="input-group-prepend">
                        <span class="input-group-text">주문자 이름</span>
                    </div>
                    <input type="text" class="form-control" id="order-name">
                </div>
                <div class="input-group mb-3">
                    <div class="input-group-prepend">
                        <label class="input-group-text">수량</label>
                    </div>
                    <select class="custom-select" id="order-count">
                        <option selected value=""> -- 수량을 선택하세요 --</option>
                        <option value="1">1</option>
                        <option value="2">2</option>
                        <option value="3">3</option>
                    </select>
                </div>
                <div class="input-group mb-3">
                    <div class="input-group-prepend">
                        <span class="input-group-text">주소</span>
                    </div>
                    <input type="text" class="form-control" id="order-address">
                </div>
                <div class="input-group mb-3">
                    <div class="input-group-prepend">
                        <span class="input-group-text">전화번호</span>
                    </div>
                    <input type="text" class="form-control" id="order-phone">
                </div>
                <div class="order">
                    <button onclick="order()" type="button" class="btn btn-primary">주문하기</button>
                </div>
            </div>
            <div class="orders">
                <table class="table">
                    <thead>
                    <tr>
                        <th scope="col">이름</th>
                        <th scope="col">수량</th>
                        <th scope="col">주소</th>
                        <th scope="col">전화번호</th>
                    </tr>
                    </thead>
                    <tbody id="orders-box">
                    <tr>
                        <td>박르탄</td>
                        <td>10000</td>
                        <td>르탄시 코딩구 열심동</td>
                        <td>010-1234-5678</td>
                    </tr>

                    </tbody>
                </table>
            </div>
        </div>
    </body>
</html>

 

'SCC 9기' 카테고리의 다른 글

[SCC 9기] 4주차  (0) 2020.08.11
[SPC 9기] 3주차  (0) 2020.07.25
[SPC 9기] 2주차  (0) 2020.07.24
[SCC 9기] 개발자를 위한 팁  (0) 2020.07.16
[SPC 9기] 0주차 - 사전준비/과제 및 이론수업  (0) 2020.06.11

* 파일관리와 이름

- 자신의 네이밍규칙을 만들고 일관적으로 해야 여러모로 좋다.

- 다른사람들과 협업할때는 서로 규칙을 정해야 한다. (역할, 업무에 맞춰)

- 파일, 폴더명, 변수명 등

- 이해하기 쉬운 이름 사용

- 호환성을 최대로 높이기 위해서는 영문,언더바 외에는 사용하지 않는 게 좋다. 언더바는 띄어쓰기 역할로 사용한다.

 

 

 

* 파이참 사용

- 파이참은, 파이썬을 코딩하기에 최적화된 프로그램이다. 하지만 파이썬외에도 여러가지도 할 수 있음. 문서 작성도 메모장보다는 워드를 선호하듯이, 코딩도 메모장으로 가능하지만  파이참 등의 프로그램을 써야 한다는 것.

- 여기서는 파이참 프로 (HTML, JAVASCRIPT 등을 지원)를 사용한다.

- 주석처리 : 블록선택 후 ctrl(command)+/(슬래시)

- 코드 정렬하기 : 블록선택 또는 커서를 두고 ctrl(command)+alt(option)+L

- 코딩한 html을 chrome에서 확인하기 : alt+f2 -> shift 누르고 크롬 클릭

- 파일 생성 : ctrl+n

- 파일 실행 : 윈도우 - ctrl+shift+f10 / MAC - ctrl+shift+r

- 라인 한 줄 삭제하기 : ctrl+y

- 더 자세한 단축키 설명 : 파이참메뉴 - help - keymap reference

https://mainia.tistory.com/5611

 

파이썬(Python) 개발도구(PyCharm) 단축키 설정과 검색하는 방법

파이썬(Python) 개발도구(PyCharm) 단축키 설정과 검색하는 방법 환경: PyCharm, Python 3.3 프로그램을 개발할 때 GUI 툴의 단축키를 외우고 활용하는 것은 필수입니다. 무엇보다 개발 생산성을 높일 수 있

mainia.tistory.com

 

* This "Exact instructions cahlenge" is so hilarious

https://www.youtube.com/watch?v=Ct-lOOUqmyY&feature=youtu.be

컴퓨터에게 명령을 내리는 방법. 단계별로 정확히 적힌 대로 실행하는 것.

 

 

* 기타

- 실제 업계에서도 잘 가져와서 쓰는게 능력

- 클라이언트단 언어는 현업 개발자들은 이런걸 잘 안하고, 뒷단의 백엔드 데이터처리쪽을 더 많이 한다.

- 코드 가독성이 매우매우 중요하다.

- 홈페이지를 만들 때, 미리 구조화를 만들고 그 구조마다 어떤 폰트와 어떤 사이즈 등을 할지 미리 체크해놓고 만들어야 한다. 처음 계획에 천천히 잘 해야함. 10시간으로 만든다면, 7시간은 계획, 3시간 코딩이 이상적이다.

 

 

 

* 파이참 코드 간단히 보기

코드 오른쪽을 보면, 더하기(또는 빼기)가 있는데, 해당 범위만큼 보이기/숨기기 기능이다. 아래처럼 card-columns 라는 div 안에 8개의 card가 있는것을 쉽게 확인할 수 있다.

카드를 부분의 더하기로 열면, 이런식으로 숨겨진 부분이 보인다.

당연히 더하기모양으로 숨겨둔 상태에서 해당 라인을 복사/잘라내기 하면 숨겨진 내용 전체가 복사/잘라내기 된다.

 

 

 

 

디버깅

 

 

프로그램 만들기는 설계, 구현, 테스팅, 디버깅 - 4단계를 거친다.

 

문제발생시

1. 에러메시지를 읽어보기, 발생한 라인의 코드 뒤를 읽어보기

2. 자바스크립트에서 발생하는 에러 메시지는 웹브라우저의 개발자도구 console 에서 확인할 있다.  많이 발생하는 에러메시지는 다음과같다.

https://www.dummies.com/web-design-development/top-10-common-javascript-error-messages/

3. 파이참에서 에러 메시지는 파이참 하단 run 창에서 확인 가능 (traceback으로 시작하는 메시지)

https://realpython.com/python-traceback/

https://python.bakyeono.net/chapter-9-2.html#922-%EC%98%A4%EB%A5%98-%EB%A9%94%EC%8B%9C%EC%A7%80

4. 에러가 없는데 원하는 결과와 다르다면, 직접 단계별로 데이터가 어떻게 변하는지 봐야 . 사용하는게 출력해서 디버깅하기, 디버거 두가지이다.

- 출력해서 디버깅하기 : 컴퓨터에게 명령을 내려 내가 가진 데이터가 바뀔대마다 데이터를 출력하도록 . 어떤 부분까지 의도대로 동작했는지 확인

- 디버거 : 디버거를 써서 위에 출력해서 디버깅하기를 편하게 있다.

크롬 디버거 튜토리얼

https://developers.google.com/web/tools/chrome-devtools/javascript

파이참 디버거 튜토리얼

https://www.jetbrains.com/ko-kr/pycharm/features/debugger.html

https://www.youtube.com/watch?v=QJtWxm12Eo0&feature=youtu.be

 

또한 발생한 상황을 재현해볼 필요도 있음.

 

 

파이참 디버깅

 

오른쪽 상단 벌레버튼이 디버그 모드임.

 

setp over 버튼 : 각각 라인을 한줄씩 실행시키고 결과를 보여준다.

 

step into 버튼 : 한줄씩 실행하는데  안에 함수 등까지 들어감. 예를들어 insert_one 은 우리가 만든 함수 아니니까, 거기까지 들어감.

 

break point

라인넘버를 클릭하면 붉은표시가 나오느데, 여기 라인을 실행하지 않음.

정확하게 이해 안됨..

 

 

 

참고 : 파이참 빨간줄

 

 

비한국어 자료를 한국어로 .

 

 

 

 

 

 

 

 

5분꿀팁 : 주석

- 주석표시할 구문을 블록지정하고, 파이참에서, 컨트롤+시프트+/ 하면 주석처리한다.  컨트롤+/ 도 된다.

- 종류는 /* , // , <!--, # 등 많음.


/* */ : CSS 주석
<!-- --> : html 주석 

 

개발자를 위한 구글 검색 노하후

https://blog.naver.com/skabin/221261551265

https://blog.naver.com/skabin/221266322873

- 아래 내용을 **나중에 스스로 읽어보면서**,  구글 검색 잘하는 방법을 구글링해보세요! **😉**

    - 추천 키워드 : `구글링하는 법'` , `how to googling for programmer` , `구글 검색 팁`

 

 

 

- 검색어 팁

    - 아래처럼 검색어를 조합해 입력해보세요!

    - 기술을 처음 배우고 싶을 때 :  '기술이름' +  'tutorial' (예. `Javascript tutorial`)

    - 기능을 찾을 때 : '기술이름' + 'how to' + '찾을 내용' (예. `Javascript how to hide div`)

    - 어떻게 사용하는지 예제를 보고 싶을 때 : '기술이름' + '내용' + 'example' (예. `Javascript onclick tutorial`)

    - 원하는 사이트명 포함해 검색할 수도 있습니다. (예: `stackoverflow  Javascript how to hide div` - stackoverflow 라는 사이트에서 검색)

- 검색 결과 중에, 좋은 자료 고르기

    - 좋은 자료를 찾으려면 경험치가 필요해요. 많이 검색해보면 자연스레 나만의 검색 노하우와 자료 판단하는 눈이 길러질 거에요.

    - 검증과정을 거쳐 잘못된 내용이 금방 수정되거나 오류 자체가 적은 사이트

        - MDN([링크](https://developer.mozilla.org/en-US/)) - 참고. MDN을 신뢰할 수 있는 이유([링크](https://zdnet.co.kr/view/?no=20171020152811))

        - 기술 공식 문서(예를 들면, [부트스트랩 컴퍼넌트 페이지](https://getbootstrap.com/docs/4.0/components/alerts/))

        - 신뢰할 수 있는 블로그(tech 회사의 기술 블로그, IT  전문 매거진)

        - stackoverflow([링크](https://stackoverflow.com/)) - 개발 QnA 사이트입니다. 전 세계적으로 많이 쓰입니다. 질문과 답변, 댓글에 사용자들이 vote 할 수 있어요. 좋은 질문과 답변에는 vote 수가 높습니다. ([링크](https://stackoverflow.com/questions/901712/how-do-i-check-whether-a-checkbox-is-checked-in-jquery))

    - 그 외에

        - 해결책뿐만 아니라 문제(에러)의 이유까지 적어두어서 내 문제와 같은지 판단할 수 있는 정보를 제공하는 글

        - 오래되지 않은 자료 - 웹의 경우, 기술이 빠르게 발전하기 때문에,  몇 년이 지난 오래된 자료는 버전 등의 문제로 내용이 달라질 수 있어요.

- 검색 결과글에 모르는 용어가 너무 많다면?

    1. 자, 일단 심호흡합시다. 침착하세요! 내가 이해할 수 있는 만큼만 부분부분 찬찬히 읽어봅시다. 

    2. 읽어보고 내가 따라서 하는게 가능한가요? 그럼 한 번 따라서 해보죠! 부분적으로 안되거나, 미심쩍은 부분, 더 알고 싶은 내용은 슬랙 채널에 질문해보세요. (단, 참고한 자료 링크도 포함해서 질문해야겠죠?)

    3. 따라하기 어렵나요? 내용을 전혀 알 수가 없나요? 좋아요. 그럼 다른 검색결과로 넘어가죠! 내 수준에 맞는 자료를 찾으면 됩니다.

    - 이렇게 해도 되냐고요? 그럼요. 우리는 프로그래밍을 배우고 있는 과정이니까 모르는 내용이 많아도 괜찮아요. 처음부터 끝까지 하나도 빠짐없이 꼭 알아야만 하는 건 아니랍니다.

- 만약 검색 결과가 잘 나오지 않는다면?

    - 여러 번 검색하기 : 처음에는 문제를 잘 모르기 때문에, 검색하면서 정보를 얻게 됩니다. 얻은 정보를 가지고 검색어를 수정해보세요.

    - 검색어 자동완성 &  연관검색어 사용 :  '자동완성 되는 검색어' 와 검색 결과 하단에 보이는 '연관 검색어'  로 검색해보세요. 구글에서는 현재 검색 결과에서 사람들이 많이 검색한 검색어와 결과가 많은 검색어를 추천해줍니다.

    - 검색어 검토하기 : 한 발 물러서서, 내가 입력한 검색어를 살펴보세요. 너무 검색어 범위가 넓지 않은지 / 여러가지 해결책 중에 내가 생각한 특정 해결책으로만 검색하고 있지 않은지!

 

 

stackoverflow - 개발자에게 아주 인기 많은 사이트.

특정 개발 언어마다 좋은 커뮤니티가 있다.

 

 

 

 

 

 

 

질문할 떄

- 현재 내 수준 알려주기. 어디까지 배웠다고.

 

 

- 스파르타코딩클럽 슬랙채널 🔥

- stackoverflow([링크](https://stackoverflow.com/)) : 개발 QnA 사이트

- 페이스북 /슬랙 개발자 커뮤니티

    - Javascript 커뮤니티에서 Python 질문하면 사람들이 어랏? 하겠죠?

    - 기술별(Javascript, Python,...) 로, 직군별(프론트엔드, 백엔드, 인프라,...)로 커뮤니티가 세분화되어 있기도 합니다.

- 기술과 관련된 Github repository 의 issue ([링크](https://github.com/pandas-dev/pandas/issues))

    - 나만 겪는 게 아닌 거 같은 버그일 때 여기에 버그알림(bug report)를 해주시면 좋겠죠?

    - repository 마다 분위기가 조금씩 다르니 기존에 있었던 issue를 보면서 파악해보세요

 

 

 

 

### 용어를 더 알아보자

 

- **CRUD 란?**

 

    CRUD(크루드, 씨알유디) 라고 읽습니다. 마법 주문이름 같기도 하죠?

 

    - 기본적인 데이터 처리 기능인 **Create**, **Read**, **Update**, **Delete** 의 두문자를 따서 **CRUD** 라고 합니다.  대부분의 소프트웨어는 이 기능을 가지고 있고요. UI가 갖추어야할 기본 기능 단위로 CRUD를 묶어 이야기합니다.

    - Create (생성) : 예. 사용자가 게시글 쓰기

    - Read (읽기) : 예. 게시글 보기

    - Update(갱신, 업데이트) : 예. 게시글 수정

    - Delete (삭제) : 예. 게시글 삭제

        - 여러분들이 나중에 소프트웨어 기능을 개발할 때 CRUD가 내가 구현해야하는 기본 세트구나! 하고 생각하시면 좋겠죠?

 

    🏃한 걸음 더! 

 

    - API를 설계하는 방식 중 하나인  `RESTful API` 에서는 HTTP method 와 CRUD 를 하나 하나씩 매핑해서 쓰기도 해요. 우리가 Read 기능은 GET 방식을 쓰자! 라고 한 것처럼요.

 

 

 

 

 

## ⌨️단축키

 

- 크롬 개발자도구

    - Windows: F12 또는 alt + ctrl + i  / Mac :  option + command + i

    - 화면에서  "마우스 오른쪽 클릭 → 검사"도 가능!

- Notion : 모든 토글을 열고 닫는 단축키

    - Windows :  alt + ctrl + t  / Mac : option + command + t

- Pycharm

    - html을 chrome으로 확인하기 단축키 : alt(또는 option)+f2 → shift 누르고 크롬 클릭

    - 파일 생성  : ctrl(또는 command) + n

    - 파일 실행 : Windows  - ctrl + shift + F10 / Mac - ctrl+ shift + r

    - 더 많은 단축키는, Pycharm 에서 확인하실 수 있습니다.

        - 화면

 

            ![https://s3-us-west-2.amazonaws.com/secure.notion-static.com/2b191127-9f3d-4f02-b9e2-c6231ce0f57c/tip_shortcut.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/2b191127-9f3d-4f02-b9e2-c6231ce0f57c/tip_shortcut.png)

 

* 크롬 개발자도구 사용하기 - html 코드 확인

크롬 사이트 화면에서, 오른클릭 -> 검사 를 누르면 다음과 같은 화면이 나온다.

여기서 body 부분을 마우스커서로 갖다대면, 사이트 화면이 위처럼 파란색으로 표시가 된다. 개발자도구에서는 해당 사이트의 html 코드를 모두 볼 수 있으며, 마우스 커서를 갖다대면 해당 코드의 위치를 확인할 수 있게 된다. 즉 이렇게 되면 해당 사이트에서 어떤 CSS 클래스를 쓰는지, 부트스트랩을 쓰는지, 어떻게 하는지 등등을 확인할 수 있다.

 

향후 프로그래밍을 할 때도, 이와 비슷한 방식을 거치면 된다. 문제를 정의/분석하고 자료를 검색하고 자료를 이해한 후 내 코드에 적용하는 것이다.

 

 

* 크롬 개발자도구 사용하기 - 콘솔로 자바스크립트 테스트하기

크롬 개발자도구에는 console이라는 탭이 있다. 이것은 해당 페이지에 대해 빠르게 자바스크립트를 테스트해볼 수 있는 도구이다. 콘솔창에 파이썬처럼 line by line 으로 코드를 입력하여 결과를 확인할 수 있다. 이런 내용들은 바로 페이지에 적용되며, 새로고침하면 사라진다.

console.log(변수);

콘솔 창에 괄호 안에 있는 값을 출력해준다. 변수 여러개를 한번에 출력할 수도 있다.

예시 : console.log("Hello world");

출력하기는 값을 확인하거나 에러를 찾을 때 주로 쓰인다.

 

 

 

 

 

 

* 개발일지 적기

내가 쓰는 블로그 등에 자유롭게 개발하면서 드는 생각, 배운것들을 적어본다. 보여주기가 아닌 스스로를 위함.

TIL (today I learned) 를 검색하면, 많은 분들의 개발일지를 볼 수 있다.

 

 

 

 

 

 

* 변수 네이밍의 일반적인 방식

변수는 담고있는 정보를 잘 표현해야 여러모로 편리하다. 또한 변수명은 숫자로 시작되거나 일부 특수문자, 띄어쓰기를 사용할수 없다. 그래서 아래와 같은 2가지 방식을 많이 사용한다.

 

camel case (이게 많이 선호됨)

- 두 단어가 연결될 , 뒤의 단어의 앞자리는 대문자로. 낙타의 혹을 닮았다고 해서 카멜 케이스라고 한다.

- firstname 아니고, firstName

 

snake case

- 두 단어가 연결될 , 언더바로 연결. 뱀 모양 같다고 해서 스네이크 케이스라고 한다.

- firstname 아니고, first_name

 

이렇게 특정 프로그래밍 언어로 프로그래밍 할 때의 일련의 지침을 Coding Convention이라고 한다. 해당 언어로 작성된 프로그램에서의 프로그래밍 스타일, 권장하는 원칙 등을 포함한다. Coding Convention을 지키면 코드가 일관된 스타일을 가지게 된다. 나만 알아볼 수 있는 코드는 1인 개발할때나 쓰자.

 

사실 Coding Convention은 엄청 많지만, 여기서는 이름 작성 형식만 따른다.

 

 

 

 

슬랙 메시지에 코드 붙여넣기

1. 짧은 코드일 경우
    - `(백틱)으로 감싸기 예. `짧은 코드`
2. 긴 코드일 경우
    - ```(백틱 세 개) 로 감싸거나
    - 메시지창 버튼 사용하기

 

 

질문할만한곳

 

- 스파르타코딩클럽 슬랙채널 🔥

- stackoverflow([링크](https://stackoverflow.com/)) : 개발 QnA 사이트

- 페이스북 /슬랙 개발자 커뮤니티

    - Javascript 커뮤니티에서 Python 질문하면 사람들이 어랏? 하겠죠?

    - 기술별(Javascript, Python,...) , 직군별(프론트엔드, 백엔드, 인프라,...)로 커뮤니티가 세분화되어 있기도 합니다.

- 기술과 관련된 Github repository issue ([링크](https://github.com/pandas-dev/pandas/issues))

    - 나만 겪는 게 아닌 거 같은 버그일 때 여기에 버그알림(bug report)를 해주시면 좋겠죠?

    - repository 마다 분위기가 조금씩 다르니 기존에 있었던 issue를 보면서 파악해보세요

 

 

 

 

 

 

- **도구(프로그래밍 툴) 잘 사용하기**

    - 도구를 손처럼 사용한다면, 생산성이 올라간답니다!  하나의 도구를 강력하게 잘 사용해보세요. 도구 단축키와 다양한 기능들을요!

    - 우리가 사용하는 Pycharm 과 크롬 개발자 도구를 잘 사용하기 위한 간단한 튜토리얼을 소개합니다.

    - Pycharm 시작하기

 

        [리소스 - 문서 | PyCharm](https://www.jetbrains.com/ko-kr/pycharm/documentation/)

 

    - 크롬 개발자 도구 사용하기

 

        [Chrome DevTools | Google Developers](https://developers.google.com/web/tools/chrome-devtools?hl=ko)

 

        [크롬 개발자 도구 101](https://lqez.github.io/blog/chrome-dev-tool-101.html)

 

        [Chrome으로 디버깅하기](https://ko.javascript.info/debugging-chrome)

 

 

 

 

 

 

> *컴퓨터가 이해할 수 있는 코드는 어느 바보나 다 짤 수 있다.

좋은 프로그래머는 사람이 이해할 수 있는 코드를 짠다.

Any fool can write code that a computer can understand. Good programmers write code that humans can understand.*

 

*—  Martin Fowler, **Refactoring**, Addison-Wesley ProfessionalJune(1999), p35*

 

코딩 컨벤션(Coding Convention) 이 무엇인가요?

 

코딩 스타일을 포함한 규약(조직체 안에서, 서로 지키도록 협의하여 정하여 놓은 규칙)입니다. 코딩 컨벤션은 사람을 위한 약속이라고 할 수 있어요. **사람의 이해를 돕고 읽고 관리하기 쉬운 코드를 만들기 위해서 존재합니다.**

 

맞춤법에 맞는 글은 훨씬 보기 좋죠? 코드도 마찬가지입니다. **코드가 일관성 있는 스타일을 가지고 있다면, 읽기도 좋고 코드의 의도를 파악하기도 편합니다.** 가독성이 좋아져 버그를 발생시킬 수 있는 코드를 발견할 확률도 높아지죠.  더불어 다른 사람들과 협업하기에도 좋습니다.

 

프로그래밍 언어마다, 회사마다 코딩 컨벤션을 가지고 있어요. 우리가 JS를 배울 때에 사용한 camelCase(단어가 연결되는 첫 글자를 대문자로 함)하고, Python에서는 snake_case( `_`로 단어 연결)를 사용한 것도 코딩 컨벤션이죠.

 

- Javascript 코딩 컨벤션들

    - 코딩 스타일 소개

 

        [코딩 스타일](https://ko.javascript.info/coding-style)

 

    - AirBnB

 

        [airbnb/javascript](https://github.com/airbnb/javascript)

 

    - NHN

 

        [코딩컨벤션](https://ui.toast.com/fe-guide/ko_CODING-CONVENSION/)

 

    - 이 외에도 `JS coding convention` 이라는 키워드로 검색하면 많은 자료를 보실 수 있어요.

- Python 코딩 컨벤션

    - PEP8 - Python 공식 스타일 가이드

 

        [PEP 8 -- Style Guide for Python Code](https://www.python.org/dev/peps/pep-0008/)

 

    - Google

 

        [google/styleguide](https://github.com/google/styleguide/blob/gh-pages/pyguide.md)

 

    - `Python coding convention` 이라는 키워드로 검색하면 더 많은 자료를 볼 수 있습니다.

- 😉지금 단계에서는 처음부터 코딩 컨벤션을 다  내 코드에 적용하려는 부담을 내려놓으세요. 이런 것들이 있구나~ 하고 키워드만 기억해놓으셔도 충분합니다!

 

 

 

코딩 컨벤션

- 조직체 안에서 코딩시 코딩 스타일을 포함한 규약을 정해 놓고, 여기에 맞게 코드를 있도록 .

- 이걸 안지킨다고 해서 오류가 나는건 아니지만, 서로 협업시 중요하다.

 

내가 코드를 며칠 지나면 내가 모름.

프로그래머는 누구나 있지만, 좋은 프로그래머가 되기는 어렵다.

 

[🏃‍♀한 걸음 더]

  • 앞으로 할 내 프로젝트에 수업시간에 배운 이름 짓기(naming) 컨벤션만 적용해보기
  • 앞으로 개발자 커리어에 관심이 있는 사람은 아래 키워드도 한 번 확인해보세요.
    • 키워드 : linter, 클린 코드(Clean Code)

linter 이용한 코딩스타일과 에러 체크하기

Clean Code: Naming

Clean code is simple and direct. Clean code reads like well-written prose.

—  Grady Booch, author of Object-Oriented Analysis and Design with Applications

 

 

 

 

 

 

 

 

 

- **가르치면서 배우기 Learning By Teaching**

    - 앞으로 스스로 공부를 위해 Learning By Teaching!  바로 가르치면서 배우기 학습방법을 소개하려고 합니다.

    - 시험기간에 친구들을 가르쳐주면서 나도 자연스럽게 학습이 되었던 기억 한 번 쯤은 있으셨을거에요. 다른 사람에게 가르치면서 나만의 언어로 정리가 되고, 내가 모르는 부분을 깨닫게 되죠.

        - 참고. Learning By Teaching 이 왜 효과적인지(교육 심리학 연구 요약 / 영문)

 

            [Learning by teaching others is extremely effective - a new study tested a key reason why](https://digest.bps.org.uk/2018/05/04/learning-by-teaching-others-is-extremely-effective-a-new-study-tested-a-key-reason-why/)

 

            [The learning benefits of teaching: A retrieval practice hypothesis](https://onlinelibrary.wiley.com/doi/abs/10.1002/acp.3410?campaign=wolearlyview)

 

    - 특히 이 과정에서 `내가 무엇을 모르는지` 깨닫는 게 된답니다. 내용은 몰라도 괜찮아요. 뭘 모르는지 알게 되면, 이제 알아보면 되니까요 😉

    - 웹을 관통하는 개념! `웹 기초 동작 원리` 20분 안에 무려 4번 복습할 수 있는 꿀팁을 알려드릴게요! 스터디에서도 응용할 수 있겠죠?

        1. 튜터님이 설명하는 웹 기초 동작원리를 열심히 듣는다.

        2. 빈 종이에 웹 동작원리를 스스로 그려본다.

        3. 옆 짝꿍 튜티에게 서로 웹 동작원리를 설명해준다.

        4. 칠판에 나와서 웹 동작원리를 그리면서 동료 튜티에게 설명해준다.

 

    [🏃한 걸음 더]

 

    - 앞서 배운 웹 동작원리 복습 꿀팁 을 실제로 해본다!

 

 

'SCC 9기' 카테고리의 다른 글

[SCC 9기] 4주차  (0) 2020.08.11
[SPC 9기] 3주차  (0) 2020.07.25
[SPC 9기] 2주차  (0) 2020.07.24
[SPC 9기] 1주차 - 프론트엔드  (0) 2020.07.16
[SPC 9기] 0주차 - 사전준비/과제 및 이론수업  (0) 2020.06.11

컨테이너를 단독으로 실행해보는 예제. 모든 명령어는 docker hub 내용을 기반으로 작성했으며 사용 환경과 업무에 따라 설정을 변경해야 한다. 해당 예시의 가상머신 서버의 ip는 192.168.1.61이며, 가상머신은 virtualbox를 사용했고, 네트워크는 "어댑터에 브릿지" 를 설정했다.

 

 

1. httpd

- httpd는 대표적인 웹서버 프로그램이다. html파일들을 올려서 홈페이지를 호스팅할 수 있다.

- 참조 : https://hub.docker.com/_/httpd

 

httpd - Docker Hub

Supported tags and respective Dockerfile links Quick reference Where to get help:the Docker Community Forums, the Docker Community Slack, or Stack Overflow Where to file issues:https://github.com/docker-library/httpd/issues Maintained by:the Docker Communi

hub.docker.com

$ docker run -dit --name my-apache-app -p 8080:80 -v /apache:/usr/local/apache2/htdocs/ httpd:2.4

이 폼은 위에 공식 도커허브에 나오는 폼의 변형이며, 호스트 서버의 /apache 경로에 html 파일을 저장하면 된다. httpd는 /usr/local/apache2/htdocs에 html파일들을 저장하기 때문. 결과는 다음과 같다. 여기에 홈페이지 통파일을 올리면 된다.

 

 

2. mariadb

- mariadb는 오픈소스 DB 중 대표적인 관계형 데이터베이스이다.

- 참조 : https://hub.docker.com/_/mariadb

 

mariadb - Docker Hub

Supported tags and respective Dockerfile links 10.4.11-bionic, 10.4-bionic, 10-bionic, bionic, 10.4.11, 10.4, 10, latest 10.3.21-bionic, 10.3-bionic, 10.3.21, 10.3 10.2.30-bionic, 10.2-bionic, 10.2.30, 10.2 10.1.43-bionic, 10.1-bionic, 10.1.43, 10.1 Quick

hub.docker.com

docker run --name some-mariadb -e MYSQL_ROOT_PASSWORD=루트비번 -itd mariadb:latest

이 폼은 위 공식 도커허브에 나오는 폼의 변형이다. mariadb에 접속할 루트 비밀번호는 직접 설정해야 한다. 또한 외부에서 연결하는 것은 고려하지 않았으므로 딱히 포트를 오픈하지는 않는다. 컨테이너가 생성되었으면, 다음과 같이 해당 컨테이너에 접속해본다.

 

docker container exec -it some-mariadb /bin/bash

접속하면 해당 컨테이너의 프롬프트로 전환되며, 여기서 database에 접속할 수 있다. 비밀번호는 컨테이너를 생성할 때 명시한 루트 비밀번호를 넣자.

 

mysql -u root -p

 

이제 아는대로 명령어를 입력해보자.

# 현재 데이터베이스 확인
show databases;

# 데이터베이스 생성
create database phones;

# 생성한 데이터베이스 사용
use phones;

# 생성한 데이터베이스 안에 테이블 있는지 확인
show tables;

# 생성한 데이터베이스 안에 테이블 생성
CREATE TABLE products ( 
           id INT(11) NOT NULL AUTO_INCREMENT, 
           name VARCHAR(100) NOT NULL, 
           comment TEXT NULL, 
           created DATETIME NOT NULL, 
           ceo VARCHAR(50) NOT NULL, 
           PRIMARY KEY(id) 
);

# 생성한 테이블의 정보 확인
DESC products;

# 테이블에 데이터 삽입하기
INSERT INTO products (name,comment,created,ceo) VALUES ('iPhone','iPhone12 Pro Max',NOW(),'tim cook');
INSERT INTO products (name,comment,created,ceo) VALUES ('Galaxy','Galaxy 20',NOW(),'lee jaeyong');

# 삽입한 데이터 확인
SELECT * FROM products;

 

3. jupyter notebook

 - 주피터 노트북은 웹브라우저에서 파이썬을 코딩하고 실행할 수 있는 프로그램이다. 사용하는 개발 라이브러리에 따라 여러가지 종류의 이미지를 다운받을 수 있다.

- 참조 : https://hub.docker.com/r/jupyter/datascience-notebook

 

Docker Hub

 

hub.docker.com

 - 해당 도커허브에서는 설치관련하여 특별한 정보는 없고, 아래 사이트에서 확인할 수 있다.

https://jupyter-docker-stacks.readthedocs.io/en/latest/index.html

 

Jupyter Docker Stacks — docker-stacks latest documentation

 

jupyter-docker-stacks.readthedocs.io

docker run -p 8888:8888 jupyter/scipy-notebook:17aba6048f44

 

이 폼은, 위 공식 사이트의 기본 예시 중 하나이다. 실행해보자. 용량이 엄청 크므로 오래 걸릴 수 있다. 해당 명령을 실행하면 아래와 같이 경로를 알려준다.

http://192.168.1.61:8888/?token=13fa3cfcfefec360f0e057b92c4c04ba893b3bfffc1a4031

IP를 192.168.1.61로 한 이유는 외부에서 접속하기 때문이며, 도커를 설치한 가상머신 자체에서 실행한다면 위에 나온대로 IP를 127.0.0.1로 하면 된다. 아래와 같은 화면이 나온다.

new 버튼을 누르고 Python3를 선택한다.

이제 다음과 같이 파이썬 코딩을 할 수 있다.

Docker Swarm Network : ingress 

 

Ingress 네트워크는 서비스의 노드들 간에 로드 밸런싱을 수행하는 Overlay 네트워크이다.  Ingress 네트워크는 도커스웜을 init 하거나 join 할 때 자동으로 생성된다. Docker Swarm에서는 서비스를 외부에 쉽게 노출하기 위해 모든 노드가 ingress라는 가상 네트워크에 속해있다. Docker swarm의 모든 노드가 노출된 포트로 클라이언트에서 요청을 하면, 해당 요청을 IPVS라는 모듈로 전달한다. IPVS는 해당 서비스에 참여하는 모든 IP주소를 추적하고 그 중 하나를 선택한 뒤, 요청을 해당 경로로 라우팅한다. 

 

포트를 노출할 때는 docker service create 명령에서 노출할 포트를 설정할 수 있다. 포트를 따로 지정하지 않는다면 매니저 노드는 30000~32767 범위 내에서 포트를 자동으로 할당한다. 

 

즉, 다시 정리하면 "서비스에서 포트를 오픈하면, 모든 노드에서 포트가 오픈되고 어떤 노드에 요청을 보내도 실행 중인 컨테이너에 자동으로 전달한다."  노드1에 컨테이너가 있는데, 노드2 IP주소:포트 로 접속하면 노드1에 있는 컨테이너에 접속이 가능하다는 것이다. 이렇게 Ingress 네트워크를 통해 load balancing이 수행된다.

 

 

위 예시에서 보면 whoami 서비스로 4567포트를 오픈한다면 3 노드 모두 4567 포트가 오픈된다. 외부에서 해당 서비스에 접속할 때, 어떤 노드를 통해서 접속하든 5개의 컨테이너 중 하나로 분산처리된다. 

 

실제 운영레벨에서는 외부에 nginx 또는 haproxy같은 로드밸런서를 둬서 하나의 IP로 전체 스웜 노드를 가리킬 수 있다.

참고 : https://docs.docker.com/engine/swarm/ingress/#/configure-an-external-load-balancer

 

* Ingress 네트워크 테스트

아래 명령을 통해 정말 어떤 아이피로 접속하든 랜덤하케 건테이너에 접속되는지 확인을 해볼 수 있다.

docker service create --name loadbalance_test --replicas 4 -p 80:80 utyk/swarm_loadbalance_check:1.0 

아래와 같이 브라우저에서 각 노드에 대한 아이피와 포트번호 80으로 접속한 후, 새로고침을 여러번 누르면 랜덤하게 컨테이너 ID가 바뀌는 것을 확인할 수 있다. 계속 F5를 연타해보자.

 

 

Docker Swarm Network : Overlay 

만약 예를들어 2개의 컨테이너가 있는데, 컨테이너1이 DB컨테이너와 접속해야 한다면, 둘다 ingress 네트워크에 배치해 외부에 노출된 상태로 통신한다면 이것은 좋은 방법이 아니다. 컨테이너간 통신을 위한 네트워크를 만들면 된다. 외부에서 접근하지 못하는 내부 컨테이너간의 네트워크에 컨테이너를 배치하여 편리함과 보안을 모두 잡을 수 있다.

# 참고 : docker_gwbridge

오버레이 네트워크(Ingress 네트워크 포함)를 개별 Docker daemon의 물리적 네트워크에 연결하는 브릿지 네트워크이다. 기본적으로, service가 실행 중인 각각의 컨테이너는 로컬 Docker daemon host의 docker_gwbridge network에 연결된다. 이 docker_gwbridge 네트워크는 docker swarm을 init 하거나 join 할 때 생성된다.

 

 

오버레이 네트워크는 Docker Swarm에 참여하는 Docker Daemon 간의 통신을 관리한다. 독립 실행 컨테이너의 네트워크를 생성하는 방법과 동일한 방식으로 생성할 수 있다. 또한 생성한 오버레이 네트워크에 Swarm service를 연결시켜 service 간에 통신을 활성화 할 수 있다. 이러한 오버레이 네트워크는 Overlay Network Driver를 사용한다.

 

버레이 네트워크를 사용하면 컨테이너는 외부에 포트를 오픈하지 않아도 되고 연결되는 다른 컨테이너와(예를들어 웹과 디비) 다른 노드에 있어도 같은 서버에 있는 것처럼 통신할 수 있다. 

# 오버레이 네트워크 생성하기
docker network create --attachable --driver overlay 이름(testbackend)

# 해당 오버레이 네트워크를 적용하여 서비스 생성하기
docker service create --name loadbalance_test2 --network=testbackend --replicas 4 -p 99:80 utyk/swarm_loadbalance_check:1.0

위와 같이 네트워크를 testbackend로 설정하면, 해당 서비스는 overlay 네트워크에 연결되며, 다른 컨테이너도 해당 testbackend에 연결했다면 서로 통신할 수 있게 된다. 아래의 inspect 명령으로 네트워크를 확인할 수 있다.

 

docker compose로 여러 컨테이너를 생성하는 경우에는 자동으로 overlay 네트워크를 생성한다. 자동으로 생성되는 overlay 네트워크의 이름을 따로 정해준다면, 아래의 예시대로 수행할 수 있다. 아래 예시는 wordpress와 db를 docker-compose로 만든 예시이다.

version: '3.1'

services:

  wordpress:
    image: wordpress
    ports:
      - 8888:80
    networks:
      - swarm_overlay_internal
    deploy:
      mode: replicated
      replicas: 3
    environment:
      WORDPRESS_DB_HOST: db
      WORDPRESS_DB_USER: wpuser
      WORDPRESS_DB_PASSWORD: examplepass
      WORDPRESS_DB_NAME: wpdb

  db:
    image: mysql:5.7
    networks:
      - swarm_overlay_internal
    deploy:
      mode: replicated
      replicas: 3
    environment:
      MYSQL_DATABASE: wpdb
      MYSQL_USER: wpuser
      MYSQL_PASSWORD: examplepass
      MYSQL_RANDOM_ROOT_PASSWORD: '1'

networks:
  swarm_overlay_internal:

 

# 참고 : docker stack 명령

Docker swarm에서는 docker-compose를 실행하려면 docker stack 명령을 사용한다. stack 은 service 의 묶음인 최상위 단위이다. 즉 동일한 컨테이너의 묶음이 service, 이러한 service를 여러개 묶은것이 stack 이다. docker stack ls, docker stack rm 등의 명령어로 관리할 수 있다. 더 이상 자세한 설명은 생략한다.

 

+ Recent posts