스파르타 코딩 클럽 1주차

Kim Sora·2021년 8월 21일

[수업 목표]

  1. 서버와 클라이언트의 역할에 대해 이해한다.
  2. HTML, CSS의 기초 지식을 이해한다. 부트스트랩을 가져다 쓸 줄 안다!
  3. Javascript 기초 문법을 익힌다.

HTML, CSS 기본 내용

  • HTML과 CSS의 개념

    • HTML은 뼈대, CSS는 꾸미기!

      HTML은 구역과 텍스트를 나타내는 코드로, CSS는 잡은 구역을 꾸며주는 것으로 생각합니다. HTML 내 style 속성으로 꾸미기를 할 수 있지만, 긴 세월동안 이것을 한데 모아 볼 수 있는 CSS 파일이 탄생하게 되었습니다. HTML 코드 내에 CSS 파일을 불러와서 적용합니다.

    • 또한 CSS를 잘 사용할 줄 아는 것과, '예쁘게' 만드는 것은 다른 영역이기 때문에(붓을 잡을 줄 아는 것과 그림을 잘 그릴 줄 아는 것의 차이), 많은 경우 웹디자이너나 퍼블리셔에게 의존하게 됩니다.

  • HTML 기초

    • HTML은 크게 head와 body로 구성되며, head안에는 페이지의 속성 정보를, body안에는 페이지의 내용을 담습니다.

    • head 안에 들어가는 대표적인 요소들: meta, script, link, title 등

      페이지의 속성을 정의하거나, 필요한 스크립트들을 부릅니다. 즉, 눈에 안 보이는 필요한 것들을 담는 것. 나중에 body 작업을 하면서 필요한 정보들을 넣어보겠습니다.

    • body 안에 들어가는 대표적인 요소들!

      • [코드스니펫] HTML기초

        ```html
        <!DOCTYPE html>
        <html lang="en">
        
        <head>
            <meta charset="UTF-8">
            <meta name="viewport" content="width=device-width, initial-scale=1.0">
            <title>스파르타코딩클럽 | HTML 기초</title>
        </head>
        
        <body>
            <!-- 구역을 나누는 태그들 -->
            <div>나는 구역을 나누죠</div>
            <p>나는 문단이에요</p>
            <ul>
                <li> bullet point!1 </li>
                <li> bullet point!2 </li>
            </ul>
        
            <!-- 구역 내 콘텐츠 태그들 -->
            <h1>h1은 제목을 나타내는 태그입니다. 페이지마다 하나씩 꼭 써주는 게 좋아요. 그래야 구글 검색이 잘 되거든요.</h1>
            <h2>h2는 소제목입니다.</h2>
            <h3>h3~h6도 각자의 역할이 있죠. 비중은 작지만..</h3>
            <hr>
            span 태그입니다: 특정 <span style="color:red">글자</span>를 꾸밀 때 써요
            <hr>
            a 태그입니다: <a href="http://naver.com/"> 하이퍼링크 </a>
            <hr>
            img 태그입니다: <img src="https://www.google.com/images/branding/googlelogo/1x/googlelogo_color_272x92dp.png" />
            <hr>
            input 태그입니다: <input type="text" />
            <hr>
            button 태그입니다: <button> 버튼입니다</button>
            <hr>
            textarea 태그입니다: <textarea>나는 무엇일까요?</textarea>
        </body>
        
        </html>
        ```

        [정렬의 중요성]
        코드의 정렬이 제대로 되어있지 않으면, 코드의 생김새를 파악할 수 없어 오류를 해결하기가 무척 어려워집니다. Pycharm에서 ctrl+alt+L (맥은 cmd+alt+L) 로 자동정렬 기능을 사용해보세요.


부트스트랩 = 예쁜 CSS 모음집

  • bootstrap이란?

    • 부트스트랩이란? 예쁜 CSS를 미리 모아둔 것 (CSS를 다룰 줄 아는 것과, 미적 감각을 발휘하여 예쁘게 만드는 것은 다른 이야기이기 때문에, 현업에서는 미리 완성된 부트스트랩을 가져다 쓰는 경우가 많습니다.)
  • bootstrap - 시작 템플릿

    [코드스니펫] 부트스트랩 시작 템플릿
    
          ```html
          <!doctype html>
          <html lang="en">
    
          <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">
    
              <!-- Optional JavaScript -->
              <!-- 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>
          ```

Javascript 맛보기

  • 자바스크립트란?

    • 프로그래밍 언어 중 하나로, 브라우저가 알아들을 수 있는 언어입니다.

      [잠깐 상식!]

      Q. 왜 브라우저는 Javascript만 알아들어요? HTML안에다 파이썬, Java 같은 언어를 써서 주면 안되나요?

      A. 불가능한 이야기는 아닙니다. 다만, 이 "역사적인 이유 & 이미 만들어진 표준"이기 때문에, 모든 브라우저는 기본적으로 Javascript를 알아듣게 설계되어있고, 모든 웹서버는 HTML+CSS+Javascript를 주게 되어있죠.

  • 자바스크립트 기초

    • 자바스크립트 - HTML 연결. 버튼을 클릭하면 경고창이 뜨게하기

      <!doctype html>
      <html lang="en">
      
      <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">
      
          <!-- Optional JavaScript -->
          <!-- 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>
      
          <link href="https://fonts.googleapis.com/css2?family=Jua&display=swap" rel="stylesheet">
      
          <style>
              * {
                  font-family: 'Jua', sans-serif;
              }
      
              .wrap {
                  margin: auto;
                  width: 900px;
              }
      
              .comment {
                  font-weight: bold;
                  color: blue;
              }
      
              .posting-box {
                  margin: 10px auto 30px auto;
                  width:500px;
      
                  border: 3px solid black;
                  border-radius: 5px;
      
                  padding: 25px;
              }
          </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="posting-box">
                  <div class="form-group">
                      <label for="exampleInputEmail1">아티클 URL</label>
                      <input type="email" class="form-control" aria-describedby="emailHelp"
                          placeholder="">
                  </div>
                  <div class="form-group">
                      <label for="exampleInputPassword1">간단 코멘트</label>
                      <input type="password" class="form-control" placeholder="">
                  </div>
                  <button type="submit" class="btn btn-primary">기사 저장</button>
              </div>
              <div class="card-columns">
                  <div class="card">
                      <img class="card-img-top"
                          src="https://d2ur7st6jjikze.cloudfront.net/offer_photos/29590/185689_medium_1525763241.jpg?1525763241"
                          alt="Card image cap">
                      <div class="card-body">
                          <a href="http://naver.com" 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://d2ur7st6jjikze.cloudfront.net/offer_photos/29590/185689_medium_1525763241.jpg?1525763241"
                          alt="Card image cap">
                      <div class="card-body">
                          <a href="http://naver.com" 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://d2ur7st6jjikze.cloudfront.net/offer_photos/29590/185689_medium_1525763241.jpg?1525763241"
                          alt="Card image cap">
                      <div class="card-body">
                          <a href="http://naver.com" 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://d2ur7st6jjikze.cloudfront.net/offer_photos/29590/185689_medium_1525763241.jpg?1525763241"
                          alt="Card image cap">
                      <div class="card-body">
                          <a href="http://naver.com" 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://d2ur7st6jjikze.cloudfront.net/offer_photos/29590/185689_medium_1525763241.jpg?1525763241"
                          alt="Card image cap">
                      <div class="card-body">
                          <a href="http://naver.com" class="card-title">여기 기사 제목이 들어가죠</a>
                          <p class="card-text">기사의 요약 내용이 들어갑니다. 동해물과 백두산이 마르고 닳도록 하느님이 보우하사 우리나라만세 무궁화 삼천리 화려강산...</p>
                          <p class="card-text comment">여기에 코멘트가 들어갑니다.</p>
                      </div>
                  </div>
              </div>
          </div>
      </body>
      
      </html>
    • 함수를 만들어두기

      function hey(){
      	alert('안녕!');
      }

      head 태그 안에 script 태그로 공간을 만들어 작성합니다.

      script 태그 내에 자바스크립트를 작성하는 것이죠
      아래 코드를 통해 간단한 사용방법을 알아봅니다.

fun

  • 버튼에 함수를 연결하기. 버튼을 누르면 함수가 불립니다.

     ```html
     <button onclick="hey()" type="button" class="btn btn-primary">기사저장</button>
     ```

Javascript 기초 문법

  • 본격적으로 문법을 배워볼까요?

    • [크롬 개발자도구]를 열어서, console 탭에 작성합니다!

      크롬 개발자도구 콘솔창은 어떤 의미?

      띄워놓은 페이지에서 빠르게 자바스크립트를 테스트해볼 수 있게, 개발자들을 위해 만들어둔 도구입니다. 새로고침하면 모두 사라진다는 사실!

      • 윈도우: F12

      • 맥: alt(option) + command + i

      • console.log(변수)

        --
        console.log(변수), 콘솔 창에 괄호 안의 값을 출력해줍니다.
        개발자가 결과값을 보기 편하도록!
        
        console.log(변수1,변수2) 로 여러 변수를 한번에 출력할 수도 있어요.
        아래를 복사해서 붙여넣어보세요.
        
        console.log("Hello World!");
    • 변수 & 기본연산

      • 변수 대입( a = 2 )의 의미: "오른쪽에 있는 것을 왼쪽에 넣는 것!"
        (2를 a라는 변수에 넣는다)

      • let으로 변수를 선언합니다.

        let num = 20
        num = 'Bob'
        
        // 변수는 값을 저장하는 박스예요.
        // 한 번 선언했으면, 다시 선언하지 않고 값을 넣습니다.
      • 사칙연산, 그리고 문자열 더하기가 기본적으로 가능합니다.

        let a = 1
        let b = 2
        
        a+b // 3
        a/b // 0.5
        
        let first = 'Bob'
        let last = 'Lee'
        
        first+last // 'BobLee'
        
        first+' '+last // 'Bob Lee'
        
        first+a // Bob1 -> 문자+숫자를 하면, 숫자를 문자로 바꾼 뒤 수행합니다.
      • 변수명은 아무렇게나?

        let first_name = 'bob' // snake case라고 합니다.
        
        또는,
        
        let firstName = 'bob' // camel case라고 합니다. 회사마다 규칙이 있죠.
        
        과 같이, 쉽게 알아볼 수 있게 쓰는 게 중요합니다.
        다른 특수문자 또는 띄워쓰기는 불가능합니다!
    • 리스트 & 딕셔너리

      • 리스트: 순서를 지켜서 가지고 있는 형태입니다.

        let a_list = []  // 리스트를 선언. 변수 이름은 역시 아무렇게나 가능!
        
        // 또는,
        
        let b_list = [1,2,'hey',3] // 로 선언 가능
        
        b_list[1] // 2 를 출력
        b_list[2] // 'hey'를 출력
        
        // 리스트에 요소 넣기
        b_list.push('헤이')
        b_list // [1, 2, "hey", 3, "헤이"] 를 출력
        
        // 리스트의 길이 구하기
        b_list.length // 5를 출력
      • 딕셔너리: 키(key)-밸류(value) 값의 묶음

        let a_dict = {}  // 딕셔너리 선언. 변수 이름은 역시 아무렇게나 가능!
        
        // 또는,
        
        let b_dict = {'name':'Bob','age':21} // 로 선언 가능
        b_dict['name'] // 'Bob'을 출력
        b_dict['age'] // 21을 출력
        
        b_dict['height'] = 180 // 딕셔너리에 키:밸류 넣기
        b_dict // {name: "Bob", age: 21, height: 180}을 출력
      • 리스트와 딕셔너리의 조합

        names = [{'name':'bob','age':20},{'name':'carry','age':38}]
        
        // names[0]['name']의 값은? 'bob'
        // names[1]['name']의 값은? 'carry'
        
        new_name = {'name':'john','age':7}
        names.push(new_name)
        
        // names의 값은? [{'name':'bob','age':20},{'name':'carry','age':38},{'name':'john','age':7}]
        // names[2]['name']의 값은? 'john'
      • 왜 필요할까요?

        순서를 표시할 수 있고, 정보를 묶을 수 있습니다.

        앞에서 언급한 <스파르타과일가게>가 정말 잘 되어서 전국에서 손님이 찾아오고 있습니다. 대기표를 작성하기 위해서 온 순서대로 이름, 휴대폰 번호를 적도록 하였는데요. 변수만을 사용한 모습은 다음과 같습니다.

        let customer_1_name = '김스파';
        let customer_1_phone = '010-1234-1234';
        let customer_2_name = '박르탄';
        let customer_2_phone = '010-4321-4321';
        ...(알아보기 힘듭니다.)

        👉딕셔너리를 활용한다면 다음과 같이 고객 별로 정보를 묶을 수 있습니다.
        let customer_1 = {'name': '김스파', 'phone': '010-1234-1234'};
        let customer_2 = {'name': '박르탄', 'phone': '010-4321-4321'};

        👉그리고 순서를 나타내기 위해 리스트를 사용하면, 이렇게나 깔끔해집니다.
        let customer = [
        {'name': '김스파', 'phone': '010-1234-1234'},
        {'name': '박르탄', 'phone': '010-4321-4321'}
        ]

        ✅보기에도 깔끔해지고, 다루기도 쉬워지고, 고객이 새로 한 명 더 오더라도 .push 함수를 이용해 간단하게 대응할 수 있습니다.

    • 기본 함수들

      • 사칙연산 외에도, 기본적으로 제공하는 여러 함수들이 존재합니다.

        왠지 이건 있을 것 같은데?(예 - 특정 문자를 바꾸고 싶다 등) 싶으면 직접 만들지 말고 구글에 먼저 찾아보세요!

        **예를 들면, '나눗셈의나머지'를 구하고 싶은 경우**
        
        let a = 20
        let b = 7
        
        a % b = 6
        **, 모든 알파벳을 대문자로 바꾸고 싶은 경우**
        
        let myname = 'spartacodingclub'
        
        myname.toUpperCase() // SPARTACODINGCLUB
        **, 특정 문자로 문자열을 나누고 싶은 경우**
        
        let myemail = 'sparta@gmail.com'
        
        let result = myemail.split('@') // ['sparta','gmail.com']
        
        result[0] // sparta
        result[1] // gmail.com
        
        let result2 = result[1].split('.') // ['gmail','com']
        
        result2[0] // gmail -> 우리가 알고 싶었던 것!
        result2[1] // com
        
        myemail.split('@')[1].split('.')[0] // gmail -> 간단하게 쓸 수도 있다!
        **특정 문자로 나누고 싶은 경우 2**
        
        let txt = '서울시-마포구-망원동'
        
        ****let names = txt.split('-'); // ['서울시','마포구','망원동']
        
        **특정 문자로 합치고 싶은 경우**
        
        let result = names.join('>'); // '서울시>마포구>망원동' (즉, 문자열 바꾸기!)
  • 함수

    • 기본 생김새

      // 만들기
      function 함수이름(필요한 변수들) {
      	내릴 명령들을 순차적으로 작성
      }
      // 사용하기
      함수이름(필요한 변수들);
    • 예시

      // 두 숫자를 입력받으면 더한 결과를 돌려주는 함수
      function sum(num1, num2) {
      	console.log('num1: ', num1, ', num2: ', num2);
      	return num1 + num2;
      }
      
      sum(3, 5); // 8
      sum(4, -1); // 3
  • 조건문

    • 20 보다 작으면 작다고, 크면 크다고 알려주는 함수

      function is_adult(age){
      	if(age > 20){
      		alert('성인이에요')
      	} else {
      		alert('청소년이에요')
      	}
      }
      
      is_adult(25)
    • if, else if, else if, else if else

      function is_adult(age){
      	if(age > 20){
      		alert('성인이에요')
      	} else if (age > 10) {
      		alert('청소년이에요')
      	} else {
      		alert('10살 이하!')
      	}
      }
      
      is_adult(12)
    • AND 조건과 OR 조건!

      // AND 조건은 이렇게
      function is_adult(age, sex){
      	if(age > 20 && sex == '여'){
      		alert('성인 여성')
      	} else if (age > 20 && sex == '남') {
      		alert('성인 남성')
      	} else {
      		alert('청소년이에요')
      	}
      }
      
      // 참고: OR 조건은 이렇게
      function is_adult(age, sex){
      	if (age > 65 || age < 10) {
      		alert('탑승하실 수 없습니다')
      	} else if(age > 20 && sex == '여'){
      		alert('성인 여성')
      	} else if (age > 20 && sex == '남') {
      		alert('성인 남성')
      	} else {
      		alert('청소년이에요')
      	}
      }
      
      is_adult(25,'남')
  • 반복문

    • 예를 들어 0부터 99까지 출력해야 하는 상황이라면!

      console.log(0)
      console.log(1)
      console.log(2)
      console.log(3)
      console.log(4)
      console.log(5)
      ...
      console.log(99)
      
      // 이렇게 쓰기엔 무리가 있겠죠? 그래서, 반복문이라는 것이 존재합니다!
    • 반복문을 이용하면 아래와 같이 단 세줄로, 출력할 수 있습니다.

      for (let i = 0; i < 100; i++) {
      	console.log(i);
      }
      for (1. 시작조건; 2. 반복조건; 3. 더하기) {
      	4. 매번실행
      }
      
      1 -> 2체크하고 -> (괜찮으면) -> 4 -> 3
      -> 2체크하고 -> (괜찮으면) -> 4 -> 3
      -> 2체크하고 -> (괜찮으면) -> 4 -> 3
      -> 2체크하고 -> (괜찮으면) -> 4 -> 3
      
      와 같은 순서로 실행됩니다.
      i가 증가하다가 반복조건에 맞지 않으면, 반복을 종료하고 빠져나옵니다.
    • 그러나 위처럼 숫자를 출력하는 경우보다는, 반복문은 주로 리스트와 함께 쓰입니다.
      아래 예시를 볼까요? 일단 아래를 복사 붙여넣기 하고, 함께 코딩해볼게요
      - [코드스니펫] 반복문 예제1

          ```jsx
          let people = ['철수','영희','민수','형준','기남','동희']
          ```
      
      ```jsx
      let people = ['철수','영희','민수','형준','기남','동희']
      
      // 이렇게 하면 리스트의 모든 원소를 한번에 출력할 수 있겠죠?
      // i가 1씩 증가하면서, people의 원소를 차례대로 불러올 수 있게 됩니다.
      for (let i = 0 ; i < people.length ; i++) {
      	console.log(people[i])
      }
      ```
    • 리스트도 그냥 리스트가 아닙니다! 딕셔너리가 들어간 리스트와 찰떡이죠
      다시 아래를 복사 붙여넣기 해볼까요?
      - [코드스니펫] 반복문 예제2

          ```jsx
          let scores = [
          	{'name':'철수', 'score':90},
          	{'name':'영희', 'score':85},
          	{'name':'민수', 'score':70},
            {'name':'형준', 'score':50},
            {'name':'기남', 'score':68},
            {'name':'동희', 'score':30},
          ]
          ```
      
      ```jsx
      let scores = [
      	{'name':'철수', 'score':90},
      	{'name':'영희', 'score':85},
      	{'name':'민수', 'score':70},
        {'name':'형준', 'score':50},
        {'name':'기남', 'score':68},
        {'name':'동희', 'score':30},
      ]
      
      for (let i = 0 ; i < scores.length ; i++) {
      	console.log(scores[i]);
      }
      
      // 이렇게 하면 리스트 내의 딕셔너리를 하나씩 출력할 수 있고,
      ```
      
      ```jsx
      for (let i = 0 ; i < scores.length ; i++) {
      	if (scores[i]['score'] < 70) {
      		console.log(scores[i]['name']);
      	}
      }
      
      // 이렇게 하면 점수가 70점 미만인 사람들의 이름만 출력할 수도 있습니다.
      ```

0개의 댓글