Normaltic 모의해킹 취업반 스터디 8기 - 1주차 과제

containerxox·2025년 4월 7일
post-thumbnail

📢 간이 로그인 페이지 만들기 (DB 연결X)

  1. login.html: Username과 Password 입력하는 로그인 페이지 HTML 생성
  2. login.scss: login.html의 SCSS
  3. login.css: login.html의 CSS
  4. login.php: 로그인 성공/실패 처리
  5. loginSuccess.php: 로그인 성공시 나타낼 페이지 (username을 동적으로 받아서 해당 페이지에 출력할 예정)



1. login.html

Username과 Password 입력하는 로그인 HTML 페이지

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Login Page</title>
    <script src="https://kit.fontawesome.com/242c7614be.js" crossorigin="anonymous"></script>
    <link rel="stylesheet" href="./css/login.css">
</head>
<body>
    <div class="login">
        <div class="login__forms">
            <!-- login form -->
            <form action="./php/login.php" method="POST" class="login__register block" id="login-in">                <h1 class="login__title">Sign In</h1>
                <div class="login__box">
                    <i class="fas fa-user login__icon"></i>
                    <input type="text" placeholder="Username" class="login__input" name="username">
                </div>
                <div class="login__box">
                    <i class="fas fa-lock login__icon"></i>
                    <input type="password" placeholder="Password" class="login__input" name="password">
                </div>
                <a href="#" class="login__forgot">Forgot Password?</a>
                <a href="#" class="login__button">Sign In</a>
                <div>
                    <span class="login__account login__account--account">Don't Have an Account?</span>
                    <span class="login__signin login__signin--signup" id="sign-up">Sign Up</span>
                </div>
            </form>

            <!-- create account form -->
            <form action="" class="login__create none" id="login-up">
                <h1 class="login__title">Create Account</h1>
                <div class="login__box">
                    <i class="fas fa-user login__icon"></i>
                    <input type="text" placeholder="Username" class="login__input">
                </div>
                <div class="login__box">
                    <i class="fa-solid fa-at login__icon"></i>
                    <input type="email" placeholder="Email" class="login__input">
                </div>
                <div class="login__box">
                    <i class="fas fa-lock login__icon"></i>
                    <input type="password" placeholder="Password" class="login__input">
                </div>
                <a href="#" class="login__button">Sign Up</a>
                <div>
                    <span class="login__account login__account--account">Already have an Account?</span>
                    <span class="login__signup login__signup--signup" id="sign-in">Sign In</span>
                </div>
                <div class="login__social">
                    <a href="#" class="login__social--icon"><i class="fab fa-facebook-f"></i></a>
                    <a href="#" class="login__social--icon"><i class="fab fa-x-twitter"></i></a>
                    <a href="#" class="login__social--icon"><i class="fab fa-google"></i></a>
                    <a href="#" class="login__social--icon"><i class="fab fa-github"></i></a>
                </div>
            </form>
        </div>
    </div>

    <script src="./js/login.js"></script>
</body>
</html>



2. login.scss

login.html의 SCSS

// 1. 변수 정의
$color-primary: #4AD395;
$color-primary-hover: #65bf97;
$color-dark: #23004d;
$color-light: #a49eac;
$color-bg: #f2f2f2;
$font: 'Open Sans', sans-serif;
$font-size-normal: 0.938rem;
$font-size-small: 0.813rem;
$font-size-big: 1.5rem;


*, ::before, ::after {
  box-sizing: border-box;
}

body {
  margin: 0;
  padding: 0;
  font-family: $font;
  font-size: $font-size-normal;
  color: $color-dark;
}

h1 {
  margin: 0;
}

a {
  text-decoration: none;
}

// sign-in, sign-up 레이아웃
.login {
  display: grid;
  place-items: center;
  height: 100vh;
  padding: 1.5rem;

  &__forms {
    width: 100%;
    max-width: 348px;
    background-color: $color-bg;
    padding: 2rem 1rem;
    border-radius: 1rem;
    text-align: center;
    box-shadow: 0 8px 20px rgba($color-dark, 0.2);
    animation: animateLogin 0.4s;
  }

  &__title {
    font-size: $font-size-big;
    margin-bottom: 2rem;
  }

  &__box {
    display: grid;
    grid-template-columns: max-content 1fr;
    column-gap: 0.5rem;
    padding: 1.125rem 1rem;
    background-color: #fff;
    margin-top: 1rem;
    border-radius: 0.5rem;
  }

  &__icon {
    font-size: $font-size-big;
    color: $color-primary;
  }

  &__input {
    border: none;
    outline: none;
    font-size: $font-size-normal;
    font-weight: 700;
    color: $color-dark;
    width: 100%;

    &::placeholder {
      color: $color-light;
    }
  }

  &__forgot {
    display: block;
    width: max-content;
    margin-left: auto;
    margin-top: 0.5rem;
    font-size: $font-size-small;
    font-weight: 600;
    color: $color-light;
  }

  &__button {
    display: block;
    padding: 1rem;
    margin: 2rem 0;
    width:100%;
    background-color: $color-primary;
    color: #fff;
    font-weight: 600;
    text-align: center;
    border-radius: 0.5rem;
    border:none;
    transition: 0.3s;

    &:hover {
      background-color: $color-primary-hover;
    }
  }

  &__account,
  &__signin,
  &__signup {
    font-weight: 600;
    font-size: $font-size-small;

    &--account {
      color: $color-dark;
    }

    &--signup {
      color: $color-primary;
      cursor: pointer;
    }
  }

  &__social {
    margin-top: 2rem;

    &--icon {
      font-size: $font-size-big;
      color: $color-dark;
      margin: 0 1rem;
    }
  }
}


.block {
  display: block;
}

.none {
  display: none;
}


@keyframes animateLogin {
  0% {
    transform: scale(1);
  }
  50% {
    transform: scale(1.1);
  }
  100% {
    transform: scale(1);
  }
}



3. login.css

login.html의 CSS

*, ::before, ::after {
  box-sizing: border-box;
}

body {
  margin: 0;
  padding: 0;
  font-family: "Open Sans", sans-serif;
  font-size: 0.938rem;
  color: #23004d;
}

h1 {
  margin: 0;
}

a {
  text-decoration: none;
}

.login {
  display: grid;
  place-items: center;
  height: 100vh;
  padding: 1.5rem;
}
.login__forms {
  width: 100%;
  max-width: 348px;
  background-color: #f2f2f2;
  padding: 2rem 1rem;
  border-radius: 1rem;
  text-align: center;
  box-shadow: 0 8px 20px rgba(35, 0, 77, 0.2);
  animation: animateLogin 0.4s;
}
.login__title {
  font-size: 1.5rem;
  margin-bottom: 2rem;
}
.login__box {
  display: grid;
  grid-template-columns: max-content 1fr;
  -moz-column-gap: 0.5rem;
       column-gap: 0.5rem;
  padding: 1.125rem 1rem;
  background-color: #fff;
  margin-top: 1rem;
  border-radius: 0.5rem;
}
.login__icon {
  font-size: 1.5rem;
  color: #4AD395;
}
.login__input {
  border: none;
  outline: none;
  font-size: 0.938rem;
  font-weight: 700;
  color: #23004d;
  width: 100%;
}
.login__input::-moz-placeholder {
  color: #a49eac;
}
.login__input::placeholder {
  color: #a49eac;
}
.login__forgot {
  display: block;
  width: -moz-max-content;
  width: max-content;
  margin-left: auto;
  margin-top: 0.5rem;
  font-size: 0.813rem;
  font-weight: 600;
  color: #a49eac;
}
.login__button {
  display: block;
  padding: 1rem;
  margin: 2rem 0;
  width: 100%;
  background-color: #4AD395;
  color: #fff;
  font-weight: 600;
  text-align: center;
  border-radius: 0.5rem;
  border:none;
  transition: 0.3s;
}
.login__button:hover {
  background-color: #65bf97;
}
.login__account, .login__signin, .login__signup {
  font-weight: 600;
  font-size: 0.813rem;
}
.login__account--account, .login__signin--account, .login__signup--account {
  color: #23004d;
}
.login__account--signup, .login__signin--signup, .login__signup--signup {
  color: #4AD395;
  cursor: pointer;
}
.login__social {
  margin-top: 2rem;
}
.login__social--icon {
  font-size: 1.5rem;
  color: #23004d;
  margin: 0 1rem;
}

.block {
  display: block;
}

.none {
  display: none;
}

@keyframes animateLogin {
  0% {
    transform: scale(1);
  }
  50% {
    transform: scale(1.1);
  }
  100% {
    transform: scale(1);
  }
}

4. login.php

로그인 성공/실패 처리
로그인 성공 시:
success.php 페이지로 이동하면서, 인코딩된 username을 URL에 포함시켜 전달.
로그인 실패 시:
↳ PHP 안에서 JavaScript 코드를 출력해서,
"로그인 실패 !" 알림창을 띄우고, 알림창 닫으면 다시 로그인 페이지(login.html)로 자동 이동.

<?php
$username = $_POST['username'];
$pw = $_POST['password'];

//디버깅(테스트)
//echo "Username: " . htmlspecialchars($username) . "<br>";
//echo "Password: " . htmlspecialchars($pw);

if($username == 'admin' && $pw == 'admin1234'){
        $username_urlencoded = urlencode($username);
        header("Location: loginSuccess.php?username=$username_urlencoded");
        exit();
}else{
        //로그인 실패한 경우
        echo "<script> alert('로그인 정보가일치하지 않습니다.');window.location.href='/project-folder/login.html'; </script>";
}
?>



5. loginSuccess.php

로그인 성공시 나타낼 페이지(username을 동적으로 받아서 페이지에서 출력)
↳ 현재 username은 admin.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Login Success!</title>
    <style>
        .container{
            display:grid;
            place-items:center;
            height: 100vh;
            padding: 1.5rem;
        }

        .contents{
            width:100%;
            max-width:348px;
            background-color:#f2f2f2;
            padding:2rem 1rem;
            border-radius:1rem;
            text-align:center;
            box-shadow: 0 8px 20px rgba(35, 0, 77, 0.2);
        }

        .welcome,.username{
            font-weight: 600;
            font-size:1.5rem;
        }
    </style>
</head>
<body>
    <div class="container">
        <div class="contents">
            <div class="welcome">Welcome !</div>
            <div class="username"><?php echo $_GET['username'];?></div> 
        </div>
    </div>
</body>
</html>

⚠️ 문제점
→ URL에 ?username=admin 처럼 사용자 정보인 username이 그대로 보이는 방식은 보안의 문제가 있다...




(참고) https://inpa.tistory.com/entry/CSS-%F0%9F%92%8D-%EB%A1%9C%EA%B7%B8%EC%9D%B8-%ED%9A%8C%EC%9B%90%EA%B0%80%EC%9E%85-%ED%8E%98%EC%9D%B4%EC%A7%80-%EC%8A%A4%ED%83%80%EC%9D%BC-%F0%9F%96%8C%EF%B8%8F-%EB%AA%A8%EC%9D%8C#%EB%A1%9C%EA%B7%B8%EC%9D%B8_/_%ED%9A%8C%EC%9B%90%EA%B0%80%EC%9E%85_%ED%8E%98%EC%9D%B4%EC%A7%80

0개의 댓글