반응형

오늘은 MySQL을 다운로드 받고 이전에 제작한 html과 DB를 연결해 로그인 기능을 구현하려 했습니다. 해당 내용을 구현하기 위해 해당 페이지를 참고했으나 제작하는 과정에서 오류가 많이 나와 오늘은 미완성인 채로 아쉽게 마무리 되었습니다. 이전에 중구난방으로 html과 css를 제작했었으나 이번에는 각각 폴더를 지정해 몰아 넣어 정리했습니다. 하지만, 저는 오늘 건강관계상 같이 토론은 못하고 내용을 전달받고 이 코드가 무슨뜻인지 주석을 달아주는 것 밖에 못했습니다. DB랑 연결해서 실험을 해봐야하는데, 오늘은 이렇게 가볍게 마치고 이번 주말에 더 공부해볼 생각입니다.

 

다음은 코드와 무슨 뜻인지 정리되어있으니 참고할 수 있을 것으로 생각됩니다. 참고로 html의 주석은 <!--  -->, 파이썬의 주석은 #, sql의 주석은 -- 입니다. 이 때 html의 주석의 내용은 <!--    --> 이 빨간 부분에 작성하게 됩니다.

 

1. main.html : 메인 페이지

<!DOCTYPE html> <!-- HTML5 문서 유형 선언 -->
<html> <!-- HTML 문서 시작 -->
<head> <!-- 문서의 메타데이터를 포함하는 요소 -->
    <title>My Ebook Page</title> <!-- 문서 제목 -->
    <link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='main.css')}}"> <!-- 외부 CSS 파일 연결 -->
</head>
<body> <!-- 문서의 본문을 포함하는 요소 -->
    <header> <!-- 문서의 머리말을 나타내는 요소 -->
        <div class="logo"> <!-- 로고 이미지를 담는 div 요소 -->
            <img src="{{ url_for('static', filename='logo.png') }}" alt="My Ebook Logo"> <!-- 로고 이미지 -->
        </div>
        <div class="search"> <!-- 검색창을 담는 div 요소 -->
            <input type="text" placeholder="Search..."> <!-- 검색창 input 요소 -->
        </div>
        <div class="user"> <!-- 로그인, 마이 페이지 링크를 담는 div 요소 -->
            <a href="/login">로그인</a> | <!-- 로그인 링크 -->
            <a href="/mypage">마이 페이지</a> <!-- 마이 페이지 링크 -->
        </div>
    </header>
    <nav> <!-- 문서의 내비게이션을 담는 요소 -->
        <ul> <!-- 내비게이션 목록을 담는 ul 요소 -->
            <li><a href="#">일반</a></li> <!-- 일반 카테고리 링크 -->
            <li><a href="#">로맨스</a></li> <!-- 로맨스 카테고리 링크 -->
            <li><a href="#">판타지</a></li> <!-- 판타지 카테고리 링크 -->
            <li><a href="#">미스테리</a></li> <!-- 미스테리 카테고리 링크 -->
            <li><a href="#">추리</a></li> <!-- 추리 카테고리 링크 -->
        </ul>
    </nav>
    <section>
        <h2>추천 도서</h2>
        <div class="book-list">
            <!-- Recommended book 1 -->
            <div class="book">
                <img src="{{ url_for('static', filename='book1.jpg') }}" alt="Book 1">
                <h3>세이노의 가르침</h3>
                <p>000년부터 발표된 그의 주옥같은 글들. 독자들이 자발적으로 만든 제본서는 물론, 전자책과 앱까지 나왔던 《세이노의 가르침》이 드디어 전국 서점에서 독자들을 마주한다. </p>
                <button>Read Now</button>
            </div>
            <!-- Recommended book 2 -->
            <div class="book">
                <img src="book2.jpg" alt="Book 2">
                <h3>NCS 통합 기본서</h3>
                <p>기획재정부 시행 ‘공공기관 채용박람회’ NCS 초청강사 집필! 공사 공단 및 금융권 필기 전형 대비</p>
                <button>Read Now</button>
            </div>
            <!-- Recommended book 3 -->
            <div class="book">
                <img src="book3.jpg" alt="Book 3">
                <h3>대한민국 청약지도</h3>
                <p>추첨제 부활! 사상 최대 특별공급 확대! 중도금 대출 완화! 당신이 알던 ‘청약’의 모든 것이 달라졌다</p>
                <button>Read Now</button>
            </div>
        </div>
        <h2>신상 도서</h2>
        <div class="book-list">
            <!-- New book 1 -->
            <div class="book">
                <img src=https://books.google.com/books/content?id=C2hMEAAAQBAJ&printsec=frontcover&img=1&zoom=1&edge=curl&source=gbs_api alt="Book 4">
                <h3>내일을 바꾸는 인생 정보</h3>
                <p>평온한 밤을 위한 인생의 클래식, 잠 못 이루는 오늘, 당신이 묻고 고전이 답하다! </p>
                <button>Read Now</button>
            </div>
            <!-- New book 2 -->
            <div class="book">
                <img src="book5.jpg" alt="Book 5">
                <h3>면접바이블</h3>
                <p>독보적 취업 유튜버 ‘면접왕 이형’의 베스트셀러 ‘면접 바이블’이 개정판으로 돌아왔다!</p>
                <button>Read Now</button>
            </div>
            <!-- New book 3 -->
            <div class="book">
                <img src="book6.jpg" alt="Book 6">
                <h3>장하준의 경제 레시피</h3>
                <p>“자유 시장의 자유에 맡겨 두면 경제가 저절로 발전할까?” “사람들이 가난한 건 게으르기 때문일까?”</p>
                <button>Read Now</button>
            </div>
        </div>
    </section>
    <footer>
        <p>Copyright &copy;
          <script>
            document.write(new Date().getFullYear())
          </script> Etevers e-book. All Rights Reserved.</p>
    </footer>
</body>
</html>

 

2. mypage.html : 마이페이지

<!DOCTYPE html> <!-- HTML5 문서 유형 선언 -->
<html> <!-- HTML 문서 시작 -->
<head> <!-- head 태그 시작 -->
  <title>My Ebook Page</title> <!-- 문서 제목 -->
  <meta name="viewport" content="width=device-width, initial-scale=1.0"> <!-- 반응형 웹을 위한 뷰포트 설정 -->
  <title>My Account</title> <!-- 탭 제목 -->
  <link href="https://fonts.googleapis.com/css2?family=Noto+Sans+KR:wght@400;500;700&display=swap" rel="stylesheet"> <!-- 구글 폰트 링크 -->
  <link rel="stylesheet" href="{{ url_for('static', filename='mypage.css')}}"> <!-- CSS 파일 링크 -->
</head> <!-- head 태그 종료 -->
<body>
  <header> <!-- 헤더 영역 -->
    <div class="logo"> <!-- 로고 이미지 -->
      <img src="{{ url_for('static', filename='logo.png') }}" alt="My Ebook Logo">
    </div>
    <div class="search"> <!-- 검색창 -->
      <input type="text" placeholder="Search...">
    </div>
    <div class="user"> <!-- 사용자 정보 -->
      <a href="#">로그아웃</a> | <a href="#">마이 페이지</a>
    </div>
  </header>
  <nav> <!-- 네비게이션 바 -->
    <ul>
      <li><a href="#">일반</a></li>
      <li><a href="#">로맨스</a></li>
      <li><a href="#">판타지</a></li>
      <li><a href="#">미스테리</a></li>
      <li><a href="#">추리</a></li>
    </ul>
  </nav>
  <main> <!-- 메인 컨텐츠 영역 -->
    <h2>나의 정보</h2> <!-- 제목 -->
    <dl> <!-- 설명 리스트 -->
      <dt>이름</dt> <!-- 설명 제목 -->
      <dd>이주성</dd> <!-- 설명 내용 -->
      <dt>이메일</dt>
      <dd>wntjd@example.com</dd>
      <dt>전화번호</dt>
      <dd>010-1234-5678</dd>
      <dt>주소</dt>
      <dd>서울시 강남구 역삼동 123-456</dd>
    </dl>
    <h2>최근 주문 내역</h2> <!-- 제목 -->
    <table> <!-- 테이블 -->
      <thead> <!-- 테이블 헤더 -->
        <tr>
          <th>주문 번호</th>
          <th>제목</th>
          <th>가격</th>
          <th>구매일자</th>
        </tr>
      </thead>
      <tbody> <!-- 테이블 바디 -->
        <tr>
          <td>12345</td>
          <td>해리포터와 마법사의 돌</td>
          <td>12,000원</td>
          <td>2022-04-18</td>
        </tr>
        <tr>
          <td>12344</td>
          <td>물고기의 언어</td>
          <td>9,000원</td>
          <td>2022-04-17</td>
        </tr>
      </tbody>
    </table>
  </main>
  <footer> <!-- 푸터 영역 -->
    <p> <!-- 문장 -->
      Copyright &copy;
      <script>
        document.write(new Date().getFullYear())  <!-- 현재 연도 자동으로 출력 -->
      </script> Etevers e-book. All Rights Reserved. <!-- 출판사 정보 -->
    </p>
  </footer>
</body>

 

3. login.html : 로그인 페이지

<!DOCTYPE html> <!-- HTML5 문서 유형 선언 -->
<html lang="en"> <!-- 문서 언어 설정 -->
<head> <!-- head 태그 시작 -->
    <meta charset="UTF-8"> <!-- 문자 인코딩 설정 -->
    <meta name="viewport" content="width=device-width, initial-scale=1.0"> <!-- 반응형 웹 설정 -->
    <title>Document</title> <!-- 문서 제목 -->
    <link href="https://fonts.googleapis.com/css2?family=Noto+Sans+KR:wght@400;500;700&display=swap" rel="stylesheet">
    <!-- 구글 폰트 링크 -->
    <script src="https://kit.fontawesome.com/53a8c415f1.js" crossorigin="anonymous"></script>
    <!-- Font Awesome 아이콘 링크 -->
    <link rel="stylesheet" href="{{ url_for('static', filename='login.css')}}"> <!-- CSS 파일 링크 -->
</head> <!-- head 태그 종료 -->
<body>
    <div class="wrap">
        <div class="login">
            <h2>Log-in</h2>
            <div class="loginaction">
                <!-- 로그인 폼 -->
                <form action="{{ url_for('login')}}" method="post" autocomplete="off">
                    <!-- 메시지 출력 -->
                    <div class="msg">{{ msg }}</div>
                    <!-- 아이디 입력 -->
                    <div class="login_id">
                        <h4>ID</h4>
                        <input type="text" name="" id="" placeholder="ID">
                    </div>
                    <!-- 비밀번호 입력 -->
                    <div class="login_pw">
                        <h4>Password</h4>
                        <input type="password" name="" id="" placeholder="Password">
                    </div>
                    <!-- 기타 옵션 -->
                    <div class="login_etc">
                        <!-- 아이디 저장 체크박스 -->
                        <div class="checkbox">
                            <input type="checkbox" name="" id=""> 아이디 저장
                        </div>
                        <!-- 비밀번호 찾기 링크 -->
                        <div class="forgot_pw">
                            <a href="">비밀번호 찾기</a>
                        </div>
                    </div>
                    <!-- 로그인 버튼 -->
                    <div class="submit">
                        <input type="submit" value="login">
                    </div>
                </form>
            </div>
            <!-- 회원가입 버튼 -->
            <div class="submit">
                <a href="/signup">
                    <input type="submit" value="Sign Up">
                </a>
            </div>
        </div>
    </div>
</body>
</html>

 

4. sginup.html : 회원가입 페이지

<!DOCTYPE html> <!-- HTML5 문서 유형 선언 -->
<html lang="en"> <!-- 문서 언어 설정 -->
<head> <!-- head 태그 시작 -->
    <meta charset="UTF-8"> <!-- 문자 인코딩 설정 -->
    <meta name="viewport" content="width=device-width, initial-scale=1.0"> <!-- 반응형 웹 설정 -->
    <title>Document</title> <!-- 문서 제목 -->
    <link href="https://fonts.googleapis.com/css2?family=Noto+Sans+KR:wght@400;500;700&display=swap" rel="stylesheet">
    <!-- 구글 폰트 링크 -->
    <script src="https://kit.fontawesome.com/53a8c415f1.js" crossorigin="anonymous"></script>
    <!-- Font Awesome 아이콘 링크 -->
    <link rel="stylesheet" href="{{ url_for('static', filename='login.css')}}"> <!-- CSS 파일 링크 -->
</head> <!-- head 태그 종료 -->
<body>
    <div class="wrap">
        <div class="login">
            <h2>Log-in</h2>
            <div class="loginaction">
                <!-- 로그인 폼 -->
                <form action="{{ url_for('login')}}" method="post" autocomplete="off">
                    <!-- 메시지 출력 -->
                    <div class="msg">{{ msg }}</div>
                    <!-- 아이디 입력 -->
                    <div class="login_id">
                        <h4>ID</h4>
                        <input type="text" name="" id="" placeholder="ID">
                    </div>
                    <!-- 비밀번호 입력 -->
                    <div class="login_pw">
                        <h4>Password</h4>
                        <input type="password" name="" id="" placeholder="Password">
                    </div>
                    <!-- 기타 옵션 -->
                    <div class="login_etc">
                        <!-- 아이디 저장 체크박스 -->
                        <div class="checkbox">
                            <input type="checkbox" name="" id=""> 아이디 저장
                        </div>
                        <!-- 비밀번호 찾기 링크 -->
                        <div class="forgot_pw">
                            <a href="">비밀번호 찾기</a>
                        </div>
                    </div>
                    <!-- 로그인 버튼 -->
                    <div class="submit">
                        <input type="submit" value="login">
                    </div>
                </form>
            </div>
            <!-- 회원가입 버튼 -->
            <div class="submit">
                <a href="/signup">
                    <input type="submit" value="Sign Up">
                </a>
            </div>
        </div>
    </div>
</body>
</html>

 

5. app.py : flask를 DB와 파이썬 연결

from flask import Flask, render_template, request, redirect, url_for, session # Flask, render_template, request, redirect, url_for, session 모듈 import
from flask_mysqldb import MySQL  # Flask MySQL 모듈 import
import MySQLdb.cursors  # MySQLdb.cursors 모듈 import
import re  # re 모듈 import
app = Flask(__name__, static_folder='static') # Flask 객체 생성
app.secret_key = 'your secret key' # 세션 보안을 위한 secret key 설정
app.config['MYSQL_HOST'] = 'localhost' # MySQL 호스트 설정
app.config['MYSQL_USER'] = 'root' # MySQL 유저 설정
app.config['MYSQL_PASSWORD'] = '1234' # MySQL 비밀번호 설정
app.config['MYSQL_DB'] = 'ebook' # MySQL 데이터베이스 설정
mysql = MySQL(app) # MySQL 객체 생성
# 홈페이지
@app.route('/')  # '/' 경로로 들어왔을 때 실행되는 함수
def home():
    return render_template('main.html')  # 'main.html' 파일을 렌더링하여 반환
# 로그인 페이지
@app.route('/login', methods=['GET', 'POST'])
def login():
    msg = ''
    if request.method == 'POST' and 'account' in request.form and 'password' in request.form:  # 만약 요청 방식이 POST이고, 폼 데이터에 'account'와 'password'가 모두 있다면
        account = request.form['account']  # POST 요청에서 account 값을 가져옴
        password = request.form['password']  # POST 요청에서 password 값을 가져옴
        cursor = mysql.connection.cursor(
            MySQLdb.cursors.DictCursor)  # MySQLdb 모듈을 사용하여 커서 생성
        cursor.execute(
            'SELECT * FROM user WHERE account = % s \
            AND password = % s', (account, password, ))  # SQL 쿼리 실행
        user = cursor.fetchone()  # 쿼리 결과를 가져옴
        if user:  # 쿼리 결과가 있으면
            session['loggedin'] = True  # 로그인 상태를 True로 변경
            session['account'] = user['account']  # 세션에 account 값을 저장
            session['password'] = user['password']  # 세션에 password 값을 저장
            msg = 'Logged in successfully !'  # 로그인 성공 메시지
            return render_template('main.html', msg=msg) # main.html 페이지를 렌더링하며 msg 값을 전달
        else:  # 쿼리 결과가 없으면
            msg = 'Incorrect username / password !'  # 로그인 실패 메시지
    return render_template('login.html', msg=msg) # login.html 페이지를 렌더링하며 msg 값을 전달

# 로그아웃
@app.route('/logout')
def logout():
    session.pop('loggedin', None)  # 'loggedin' 세션 삭제
    session.pop('account', None)  # 'account' 세션 삭제
    session.pop('password', None)  # 'password' 세션 삭제
    return redirect(url_for('login'))  # login 페이지로 리다이렉트
# 회원가입 페이지
@app.route('/signup', methods=['GET', 'POST'])
def signup():
    msg = ''
    # 다음 코드는 POST 요청이 들어왔을 때, 모든 필드가 채워졌는지 확인하고, 채워졌다면 해당 값을 변수에 저장한다.
    if request.method == 'POST' \
            and 'account' in request.form \
            and 'password' in request.form \
            and 'username' in request.form \
            and 'email' in request.form \
            and 'gender' in request.form \
            and 'birth' in request.form \
            and 'phonenumber' in request.form:
        account = request.form['account']
        password = request.form['password']
        username = request.form['username']
        email = request.form['email']
        gender = request.form['gender']
        birth = request.form['birth']
        phonenumber = request.form['phonenumber']
        # 다음 코드는 MySQL 데이터베이스에 연결하고, 입력한 계정이 이미 존재하는지 확인한다.
        cursor = mysql.connection.cursor(MySQLdb.cursors.DictCursor)
        cursor.execute(
            'SELECT * FROM user WHERE account = % s', (account, ))
        user = cursor.fetchone()
        # 다음 코드는 이미 존재하는 계정이라면 에러 메시지를 출력한다.
        if user:
            msg = '이미 존재하는 계정입니다 !'
        # 다음 코드는 이메일 형식이 올바르지 않다면 에러 메시지를 출력한다.
        elif not re.match(r'[^@]+@[^@]+\.[^@]+', email):
            msg = 'Email 형식에 맞지 않습니다 !'
        # 다음 코드는 이름이 올바른 형식이 아니라면 에러 메시지를 출력한다.
        elif not re.match(r'[A-Za-z0-9]+', username):
            msg = 'name must contain only characters and numbers !'
        # 다음 코드는 모든 조건을 만족한다면, 데이터베이스에 새로운 사용자 정보를 추가한다.
        else:
            cursor.execute('INSERT INTO user VALUES \
                (% s, % s, % s, % s, % s, % s, % s)',
                           (account, password, username, email, gender, birth, phonenumber))
            mysql.connection.commit()
            msg = '가입을 환영합니다 !'
    # 다음 코드는 POST 요청이 들어왔지만, 필드가 채워지지 않았다면 에러 메시지를 출력한다.
    elif request.method == 'POST':
        msg = 'Please fill out the form !'
    # 다음 코드는 GET 요청이 들어왔을 때, 회원가입 페이지를 렌더링한다.
    return render_template('signup.html', msg=msg)
# 메인 페이지
@app.route("/main")
def index():
    if 'loggedin' in session:  # 로그인 여부를 세션에서 확인
        return render_template("main.html")  # 로그인 되어 있으면 main.html 페이지 렌더링
    return redirect(url_for('login'))  # 로그인 되어 있지 않으면 login 페이지로 리다이렉트
# 마이페이지
@app.route('/mypage.html')  # '/mypage.html' 경로로 요청이 들어오면
def mypage():  # mypage 함수 실행
    return render_template('mypage.html')  # mypage.html 파일을 렌더링하여 반환

if __name__ == '__main__':  # 현재 파일이 실행되는 메인 파일일 경우
    app.run(debug=True)  # Flask 애플리케이션을 debug 모드로 실행

 

6. user.sql : DB

create database ebook default character set utf8 collate utf8_general_ci; -- ebook 데이터베이스 생성 및 문자 인코딩 설정
use ebook -- ebook 데이터베이스 사용
create table user ( -- user 테이블 생성
    account char(30) not null, -- 계정(아이디) 열 생성, null 값 불가능
    password varchar(255) not null, -- 비밀번호 열 생성, null 값 불가능
    username varchar(50) not null, -- 사용자 이름 열 생성, null 값 불가능
    email varchar(100) not null, -- 이메일 열 생성, null 값 불가능
    gender varchar(10) not null, -- 성별 열 생성, null 값 불가능
    birth date not null, -- 생년월일 열 생성, null 값 불가능
    phonenumber varchar(30), -- 전화번호 열 생성, null 값 가능
    primary key(account) -- account 열을 기본키로 설정
) engine=InnoDB default charset=utf8; -- InnoDB 스토리지 엔진 사용 및 문자 인코딩 설정
반응형