반응형

1. 기본 문법

const newArray = originalArray.filter(callback(element[, index[, array]])[, thisArg]);
  • originalArray: 원본 배열.
  • callback: 배열의 각 요소에 대해 실행할 함수.
    • element: 현재 처리 중인 요소.
    • index (선택): 현재 요소의 인덱스.
    • array (선택): filter()를 호출한 배열 자체.
  • thisArg (선택): callback 함수 내에서 사용될 this 값.

2. 콜백 함수의 역할

콜백 함수는 배열의 각 요소마다 호출되며, 각 호출 시 다음과 같이 전달됩니다:

  • element: 현재 요소.
  • index: 현재 요소의 인덱스.
  • array: 원본 배열 전체.

콜백 함수는 true 또는 false를 반환해야 합니다.

  • true를 반환하면, 해당 요소는 새 배열에 포함됩니다.
  • false를 반환하면, 해당 요소는 제외됩니다.

예를 들어:

const numbers = [1, 2, 3, 4, 5];
const evenNumbers = numbers.filter((num) => {
  return num % 2 === 0;  // 짝수일 경우에만 true 반환
});
console.log(evenNumbers); // [2, 4]
 

3. thisArg 매개변수

두 번째 인자로 전달하는 thisArg는 콜백 함수 내부에서 this로 사용됩니다.

예를 들어, 객체의 속성을 비교할 때 유용합니다:

 
const context = { threshold: 10 };
const numbers = [5, 12, 8, 130, 44];

const filtered = numbers.filter(function(num) {
  return num > this.threshold;
}, context);

console.log(filtered); // [12, 130, 44]

4. 다양한 사용 예제

4.1. 배열 내의 객체 필터링

객체 배열에서 특정 조건을 만족하는 객체만 골라낼 수 있습니다.

const users = [
  { name: 'Alice', age: 25 },
  { name: 'Bob', age: 30 },
  { name: 'Charlie', age: 35 }
];

const usersOver30 = users.filter(user => user.age > 30);
console.log(usersOver30); // [ { name: 'Charlie', age: 35 } ]

4.2. 중첩 배열에서 특정 요소 제거

특정 값이 포함된 배열을 제거하는 경우:

const fruits = ['apple', 'banana', 'orange', 'banana'];
const filteredFruits = fruits.filter(fruit => fruit !== 'banana');
console.log(filteredFruits); // ['apple', 'orange']

4.3. 조건에 따른 복합 필터링

여러 조건을 조합하여 필터링할 수도 있습니다.

const items = [
  { name: 'Book', price: 12, inStock: true },
  { name: 'Pen', price: 2, inStock: false },
  { name: 'Notebook', price: 5, inStock: true }
];

const affordableInStock = items.filter(item => item.inStock && item.price < 10);
console.log(affordableInStock); // [ { name: 'Notebook', price: 5, inStock: true } ]

5. filter()의 특성과 주의 사항

  • 불변성 유지: filter()는 원본 배열을 변경하지 않고, 조건에 맞는 요소들로 새 배열을 생성합니다.
  • 희소 배열(Sparse Array): 배열 내에 비어있는 요소(holes)가 있는 경우, 해당 빈 요소들은 callback 함수에서 호출되지 않습니다.
  • 반환 값: 조건에 맞는 요소가 하나도 없으면 빈 배열 ([]) 을 반환합니다.
  • 고차 함수(Higher-Order Function): filter()는 콜백 함수를 인자로 받으므로, 다른 함수와 조합하여 사용하기 좋습니다.
  • 실행 순서: 배열의 각 요소에 대해 순서대로 콜백 함수가 호출됩니다. 따라서 인덱스에 의존하는 조건문 작성 시 주의해야 합니다.

6. filter()와 다른 배열 메서드 비교

  • map(): 배열의 각 요소를 변환하여 새 배열을 만듭니다. (길이는 동일)
  • reduce(): 배열의 모든 요소를 누적하여 하나의 값으로 축소합니다.
  • find(): 조건을 만족하는 첫 번째 요소만 반환합니다.
  • forEach(): 배열의 각 요소에 대해 주어진 함수를 실행하지만, 반환 값을 저장하지 않습니다.

filter()는 배열에서 특정 조건에 맞는 요소들만 골라내는 데에 최적화되어 있으며, 반환된 배열은 원본 배열과 별개이므로 후속 조작에 안전하게 사용할 수 있습니다.


7. 결론

JavaScript의 filter() 메서드는 배열 데이터를 다룰 때 매우 강력하고 유연하게 사용할 수 있는 도구입니다. 다양한 상황에서 원하는 조건에 맞는 데이터를 선별하고, 원본 배열을 건드리지 않으면서 새로운 배열을 생성할 수 있기 때문에, 코드의 가독성과 유지보수성을 높이는 데 큰 도움을 줍니다.

이처럼 filter()의 기본 사용법부터 고급 사용법까지 이해하면, 복잡한 데이터 필터링 작업도 간단하고 직관적으로 해결할 수 있습니다.

반응형
반응형

JavaScript에서 data 속성은 주로 HTML 요소에 사용자 정의 데이터를 저장할 때 사용하는 HTML5의 커스텀 데이터 속성을 의미합니다. 이 속성들은 data-로 시작하며, JavaScript를 통해 쉽게 접근하고 조작할 수 있습니다.

HTML5 커스텀 데이터 속성이란?

  • 정의: HTML 요소에 추가 정보를 저장할 수 있는 속성입니다.
  • 형식: data-키이름="값" 형식으로 사용합니다.
  • 사용 목적: 별도의 시각적 표현이 필요 없는, 추가적인 정보를 요소에 담고 싶을 때 사용합니다.

JavaScript에서의 접근 방법

HTML 요소에 설정된 커스텀 데이터 속성은 JavaScript의 dataset 프로퍼티를 통해 접근할 수 있습니다.

  • data- 뒤의 이름은 카멜 표기법으로 변환되어 접근합니다.
    예) data-user-id → element.dataset.userId

예제

다음은 HTML 요소에 커스텀 데이터 속성을 설정하고, JavaScript에서 읽고 수정하는 예제입니다.

 
<!-- HTML -->
<div id="userInfo" data-user-id="101" data-user-name="JohnDoe">
  사용자 정보
</div>
 
// JavaScript
const userInfo = document.getElementById('userInfo');

// data 속성 읽기
console.log(userInfo.dataset.userId);    // 출력: "101"
console.log(userInfo.dataset.userName);  // 출력: "JohnDoe"

// data 속성 변경
userInfo.dataset.userName = 'JaneDoe';
console.log(userInfo.dataset.userName);  // 출력: "JaneDoe"

요약

  • HTML: <div id="userInfo" data-user-id="101" data-user-name="JohnDoe"></div>
  • JavaScript: document.getElementById('userInfo').dataset를 통해 data-user-id는 userId, data-user-name은 userName으로 접근

이처럼 HTML5의 data 속성을 활용하면, 요소에 추가적인 정보를 쉽게 저장하고 JavaScript에서 이를 읽어 동적으로 활용할 수 있습니다.

 

여기서 조금더 알아봅시다.

 

id 속성 없이도, CSS 선택자를 이용하여 data-user-id="101" 속성을 가진 요소를 선택할 수 있습니다.
이를 위해 JavaScript의 document.querySelector나 document.querySelectorAll 메서드를 사용할 수 있습니다.

그리고 추가로 해당 데이터의 값을 변경하는 법을 알아보도록 하겠습니다.

 

예제를 통해 알아보겠습니다.

HTML 코드 예제

<div data-user-id="101" data-user-name="JohnDoe">
  사용자 정보
</div>

JavaScript 코드 예제

 
// data-user-id가 "101"인 요소 선택 (첫 번째 요소만 선택)
const element = document.querySelector('[data-user-id="101"]');

if (element) {
  // 기존 값 확인
  console.log(element.dataset.userId); // 출력: "101"
  
  // data-user-id 값을 수정하는 방법 1: dataset 프로퍼티 사용
  element.dataset.userId = '202';
  console.log(element.dataset.userId); // 출력: "202"
  
  // data-user-id 값을 수정하는 방법 2: setAttribute 메서드 사용
  element.setAttribute('data-user-id', '303');
  console.log(element.getAttribute('data-user-id')); // 출력: "303"
}

설명

  1. 요소 선택하기:
    • document.querySelector('[data-user-id="101"]')를 사용하여 data-user-id 속성의 값이 "101"인 요소를 선택합니다.
    • 여러 요소를 선택하려면 document.querySelectorAll('[data-user-id="101"]')를 사용하여 NodeList를 받을 수 있습니다.
  2. 값 수정하기:
    • 방법 1: dataset 프로퍼티를 사용하여 element.dataset.userId로 접근한 후 원하는 값으로 변경합니다.주의: data-user-id 속성은 dataset에서는 userId로 접근합니다.
    • 방법 2: setAttribute 메서드를 사용하여 data-user-id 속성을 직접 수정할 수 있습니다.

이처럼, id 없이도 data 속성을 가진 요소를 쉽게 선택하고, 값을 읽거나 수정할 수 있습니다.

반응형
반응형

1. 기본적인 this 동작 방식

this는 함수가 호출되는 방식에 따라 값이 달라집니다. 주요한 패턴은 다음과 같습니다.

1.1 전역 컨텍스트에서의 this

 
console.log(this); // 브라우저에서는 Window 객체, Node.js에서는 global 객체
 

전역에서 this를 출력하면 브라우저 환경에서는 window 객체를, Node.js 환경에서는 global 객체를 참조합니다.


1.2 일반 함수에서의 this (엄격 모드와 일반 모드)

function showThis() {
    console.log(this);
}
showThis(); // 브라우저에서는 window, Node.js에서는 global

"use strict";
function strictShowThis() {
    console.log(this);
}
strictShowThis(); // undefined (엄격 모드에서는 `this`가 undefined)
  • 일반 모드: this는 전역 객체(window 또는 global)를 참조합니다.
  • 엄격 모드("use strict"): this는 undefined가 됩니다.

1.3 객체의 메서드에서의 this

const obj = {
    name: "Alice",
    showThis: function () {
        console.log(this.name);
    }
};
obj.showThis(); // "Alice"
  • 메서드를 호출한 객체(obj)가 this가 됩니다.

하지만 함수 내부에서 새로운 함수가 호출되면 this가 바뀔 수 있습니다.

const obj = {
    name: "Alice",
    showThis: function () {
        function inner() {
            console.log(this);
        }
        inner(); // 일반 함수로 호출되므로 `this`는 `window` 또는 `undefined`(엄격 모드) 
    }
};
obj.showThis();
  • 해결 방법:
    1. self 또는 that 변수를 활용 (var self = this;)
    2. .bind(this) 사용
    3. 화살표 함수 사용 (아래에서 설명)

2. 생성자 함수와 this

function Person(name) {
    this.name = name;
}

const p1 = new Person("Alice");
console.log(p1.name); // "Alice"
  • new 키워드를 사용하여 생성자 함수를 호출하면 this는 새로 생성된 객체를 참조합니다.

만약 new 없이 호출하면 this가 전역 객체를 가리키거나(비엄격 모드) undefined(엄격 모드)로 설정됩니다.

const p2 = Person("Bob"); // new 없이 호출
console.log(p2); // undefined
console.log(global.name); // "Bob" (Node.js의 경우)

해결 방법:

  • new 없이 호출될 경우 this가 올바른 객체를 참조하도록 강제하는 패턴 사용
function Person(name) {
    if (!(this instanceof Person)) {
        return new Person(name);
    }
    this.name = name;
}

3. 화살표 함수에서의 this

화살표 함수는 일반 함수와 다르게 this를 바인딩하지 않고, 자신을 포함하는 외부 스코프(렉시컬 스코프)의 this를 사용합니다.

const obj = {
    name: "Alice",
    showThis: function () {
        const arrow = () => {
            console.log(this.name);
        };
        arrow();
    }
};
obj.showThis(); // "Alice"
  • arrow 함수는 obj.showThis()의 this를 유지합니다.

하지만 화살표 함수는 생성자 함수로 사용할 수 없습니다.

const Person = (name) => {
    this.name = name;
};
const p = new Person("Alice"); // TypeError: Person is not a constructor

4. bind, call, apply를 이용한 this 변경

4.1 call()과 apply()

call()과 apply()를 사용하면 특정한 this를 설정하여 함수를 실행할 수 있습니다.

 
function showName() {
    console.log(this.name);
}

const user = { name: "Alice" };
showName.call(user); // "Alice"
showName.apply(user); // "Alice"
  • call(thisArg, arg1, arg2, ...) → 개별 인자로 전달
  • apply(thisArg, [arg1, arg2, ...]) → 배열로 전달

4.2 bind()

bind()는 새로운 함수를 반환하여 이후에 this가 고정된 상태로 실행되도록 합니다.

function showName() {
    console.log(this.name);
}
const user = { name: "Alice" };

const boundFn = showName.bind(user);
boundFn(); // "Alice"
  • bind()를 사용하면 this가 영구적으로 고정됩니다.

5. 이벤트 핸들러에서의 this

5.1 일반 함수에서의 this

const button = document.querySelector("button");
button.addEventListener("click", function () {
    console.log(this); // 클릭된 button 요소를 참조
});

5.2 화살표 함수에서의 this

button.addEventListener("click", () => {
    console.log(this); // window 객체를 참조
});
  • 화살표 함수는 이벤트 핸들러로 사용될 때 this가 window가 되어 문제가 발생할 수 있습니다.

해결 방법:

  • bind(this)를 사용하거나, 일반 함수 표현식을 사용

6. 클래스에서의 this

클래스 메서드에서는 this가 자동으로 인스턴스를 가리킵니다.

class Person {
    constructor(name) {
        this.name = name;
    }
    showName() {
        console.log(this.name);
    }
}
const p = new Person("Alice");
p.showName(); // "Alice"

하지만, 클래스 내부에서 setTimeout 등을 사용할 경우 this가 바뀔 수 있습니다.

class Timer {
    start() {
        setTimeout(function () {
            console.log(this); // window 또는 undefined
        }, 1000);
    }
}
const t = new Timer();
t.start();
  • 해결 방법: bind(this) 또는 화살표 함수 사용
class Timer {
    start() {
        setTimeout(() => {
            console.log(this); // Timer 객체 유지
        }, 1000);
    }
}

7. 정리

호출 방식this 값

전역 실행 (일반 모드) window (브라우저) / global (Node.js)
전역 실행 (엄격 모드) undefined
객체의 메서드 해당 객체
화살표 함수 외부 this를 따름 (렉시컬 this)
생성자 함수 새로 생성된 객체 (new 사용 시)
call / apply 명시적으로 지정된 객체
bind 영구적으로 고정된 this
DOM 이벤트 핸들러 (일반 함수) 이벤트가 발생한 요소
DOM 이벤트 핸들러 (화살표 함수) window

this는 문맥에 따라 다르게 동작하므로, 실행 방식과 함수 선언 형태를 잘 이해해야 합니다.

 

특히 클래스 사용시에 전달값을 함수로해서 this를 사용하는 경우에 바인등을 주의깊게 살피지 않으시면 뜬금없이 에러가 발생하므로 주의깊게 사용하셔야 이상한 동작을 할 경우가 줄어듭니다.

반응형
반응형

먼저 JWT의 기본 개념에 대해 설명드리겠습니다:

JWT는 당사자간에 정보를 JSON 객체로 안전하게 전송하기 위한 간단한 방법입니다. 이 정보는 디지털 서명되어 있어 신뢰할 수 있습니다.

JWT의 구조:

  1. Header: 토큰 유형과 사용된 서명 알고리즘
  2. Payload: 전달하려는 데이터(클레임)
  3. Signature: 헤더와 페이로드를 기반으로 한 서명

주요 사용 사례:

  • 인증(Authentication)
  • 정보 교환
  • 상태 유지(Stateless) 서버 구현

 

using System;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
using Microsoft.IdentityModel.Tokens;

public class JwtAuthenticationExample : System.Web.UI.Page
{
    private readonly string _secretKey = "your-256-bit-secret-key-here";
    
    // JWT 토큰 생성 메서드
    protected string GenerateJwtToken(string userId, string username, string role)
    {
        var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_secretKey));
        var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);

        var claims = new[]
        {
            new Claim(JwtRegisteredClaimNames.Sub, userId),
            new Claim(JwtRegisteredClaimNames.UniqueName, username),
            new Claim(ClaimTypes.Role, role),
            new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
            new Claim(JwtRegisteredClaimNames.Iat, DateTime.UtcNow.ToString())
        };

        var token = new JwtSecurityToken(
            issuer: "your-issuer",
            audience: "your-audience",
            claims: claims,
            expires: DateTime.UtcNow.AddHours(1),
            signingCredentials: credentials
        );

        return new JwtSecurityTokenHandler().WriteToken(token);
    }

    // JWT 토큰 검증 메서드
    protected bool ValidateToken(string token)
    {
        var tokenHandler = new JwtSecurityTokenHandler();
        var key = Encoding.ASCII.GetBytes(_secretKey);

        try
        {
            tokenHandler.ValidateToken(token, new TokenValidationParameters
            {
                ValidateIssuerSigningKey = true,
                IssuerSigningKey = new SymmetricSecurityKey(key),
                ValidateIssuer = true,
                ValidIssuer = "your-issuer",
                ValidateAudience = true,
                ValidAudience = "your-audience",
                ValidateLifetime = true,
                ClockSkew = TimeSpan.Zero
            }, out SecurityToken validatedToken);

            return true;
        }
        catch
        {
            return false;
        }
    }

    // 로그인 처리 예제
    protected void LoginButton_Click(object sender, EventArgs e)
    {
        // 실제 구현에서는 데이터베이스에서 사용자 검증
        string userId = "123";
        string username = "testuser";
        string role = "user";

        string token = GenerateJwtToken(userId, username, role);
        
        // 토큰을 클라이언트에 전달 (예: 쿠키로 저장)
        Response.Cookies.Add(new HttpCookie("jwt_token", token));
    }

    // 보호된 리소스 접근 예제
    protected void Page_Load(object sender, EventArgs e)
    {
        HttpCookie tokenCookie = Request.Cookies["jwt_token"];
        
        if (tokenCookie != null && ValidateToken(tokenCookie.Value))
        {
            // 인증된 사용자 처리
            // 보호된 리소스에 접근 허용
        }
        else
        {
            // 인증되지 않은 사용자 처리
            Response.Redirect("~/Login.aspx");
        }
    }
}

// Web.config 설정
/*
<configuration>
  <system.web>
    <authentication mode="None" />
  </system.web>
  <system.webServer>
    <modules>
      <remove name="FormsAuthentication" />
    </modules>
  </system.webServer>
</configuration>
*/

 

 

이 예제 코드의 주요 특징:

  1. 토큰 생성 (GenerateJwtToken):
    • 사용자 ID, 이름, 역할 등의 클레임 포함
    • 토큰 만료 시간 설정 (1시간)
    • HMAC SHA256 알고리즘으로 서명
  2. 토큰 검증 (ValidateToken):
    • 토큰의 서명 검증
    • 발급자(issuer)와 수신자(audience) 검증
    • 만료 시간 검증
  3. 실제 사용 예시:
    • 로그인 시 토큰 생성
    • 보호된 페이지 접근 시 토큰 검증

3번의 로그인 버튼 쪽이랑 페이지 로드 부분은 별로 신경 안 쓰셔도 됩니다. 

보통 클라이언트에서 위에 두개의 함수를 이용하여 토큰을 생성하고 검증하는 부분으로 사용되기 때문입니다.

 

구현 시 주의사항:

  1. 시크릿 키는 안전하게 보관해야 합니다
  2. HTTPS를 사용하여 통신해야 합니다
  3. 토큰 만료 시간을 적절히 설정해야 합니다
  4. 필요한 패키지를 설치해야 합니다:

 

 

 

JWT를 활용한 클라이언트 구현을 자세히 만들어보겠습니다.

<!-- Login.aspx -->
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Login.aspx.cs" Inherits="JwtExample.Login" %>

<!DOCTYPE html>
<html>
<head>
    <title>로그인</title>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
    <style>
        .login-container {
            max-width: 400px;
            margin: 50px auto;
            padding: 20px;
            border: 1px solid #ddd;
            border-radius: 5px;
        }
        .form-group {
            margin-bottom: 15px;
        }
        .form-group label {
            display: block;
            margin-bottom: 5px;
        }
        .form-group input {
            width: 100%;
            padding: 8px;
            border: 1px solid #ddd;
            border-radius: 4px;
        }
        .error-message {
            color: red;
            margin-top: 10px;
            display: none;
        }
    </style>
</head>
<body>
    <div class="login-container">
        <h2>로그인</h2>
        <div class="form-group">
            <label for="username">사용자명:</label>
            <input type="text" id="username" name="username" />
        </div>
        <div class="form-group">
            <label for="password">비밀번호:</label>
            <input type="password" id="password" name="password" />
        </div>
        <button onclick="login()">로그인</button>
        <div id="errorMessage" class="error-message"></div>
    </div>

    <script>
        function login() {
            const username = $('#username').val();
            const password = $('#password').val();

            // 로그인 요청
            $.ajax({
                url: 'Login.aspx/LoginUser',
                type: 'POST',
                contentType: 'application/json',
                data: JSON.stringify({
                    username: username,
                    password: password
                }),
                success: function(response) {
                    if (response.d.success) {
                        // JWT 토큰 저장
                        localStorage.setItem('jwt_token', response.d.token);
                        // 사용자 정보 저장
                        localStorage.setItem('user_info', JSON.stringify(response.d.userInfo));
                        // 메인 페이지로 리다이렉트
                        window.location.href = 'Dashboard.aspx';
                    } else {
                        $('#errorMessage').text(response.d.message).show();
                    }
                },
                error: function() {
                    $('#errorMessage').text('로그인 중 오류가 발생했습니다.').show();
                }
            });
        }
    </script>
</body>
</html>

<!-- Dashboard.aspx -->
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Dashboard.aspx.cs" Inherits="JwtExample.Dashboard" %>

<!DOCTYPE html>
<html>
<head>
   <title>대시보드</title>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
    <style>
        .dashboard-container {
            max-width: 800px;
            margin: 20px auto;
            padding: 20px;
        }
        .header {
            display: flex;
            justify-content: space-between;
            align-items: center;
            margin-bottom: 20px;
        }
        .user-info {
            margin-bottom: 20px;
        }
        .protected-content {
            border: 1px solid #ddd;
            padding: 20px;
            border-radius: 5px;
        }
    </style>
</head>
<body>
    <div class="dashboard-container">
        <div class="header">
            <h2>대시보드</h2>
            <button onclick="logout()">로그아웃</button>
        </div>
        <div id="userInfo" class="user-info"></div>
        <div class="protected-content">
            <h3>보호된 컨텐츠</h3>
            <div id="protectedData"></div>
        </div>
    </div>

    <script>
        // 페이지 로드 시 인증 확인
        $(document).ready(function() {
            checkAuth();
            loadUserInfo();
            loadProtectedData();
        });

        // 인증 확인
        function checkAuth() {
            const token = localStorage.getItem('jwt_token');
            if (!token) {
                window.location.href = 'Login.aspx';
                return;
            }
        }

        // 사용자 정보 로드
        function loadUserInfo() {
            const userInfo = JSON.parse(localStorage.getItem('user_info'));
            if (userInfo) {
                $('#userInfo').html(`
                    <p>사용자명: ${userInfo.username}</p>
                    <p>역할: ${userInfo.role}</p>
                `);
            }
        }

        // 보호된 데이터 로드
        function loadProtectedData() {
            $.ajax({
                url: 'Dashboard.aspx/GetProtectedData',
                type: 'POST',
                contentType: 'application/json',
                headers: {
                    'Authorization': 'Bearer ' + localStorage.getItem('jwt_token')
                },
                success: function(response) {
                    $('#protectedData').html(response.d.data);
                },
                error: function(xhr) {
                    if (xhr.status === 401) {
                        // 인증 실패 시 로그인 페이지로 리다이렉트
                        logout();
                    } else {
                        $('#protectedData').html('데이터 로드 중 오류가 발생했습니다.');
                    }
                }
            });
        }

        // 로그아웃
        function logout() {
            localStorage.removeItem('jwt_token');
            localStorage.removeItem('user_info');
            window.location.href = 'Login.aspx';
        }

        // API 요청을 위한 공통 함수
        function apiRequest(url, method, data) {
            return $.ajax({
                url: url,
                type: method,
                contentType: 'application/json',
                headers: {
                    'Authorization': 'Bearer ' + localStorage.getItem('jwt_token')
                },
                data: JSON.stringify(data)
            });
        }
    </script>
</body>
</html>
 

이 클라이언트 구현의 주요 특징과 기능을 설명드리겠습니다:

  1. 로그인 페이지 (Login.aspx):
    • 사용자명과 비밀번호 입력 폼 제공
    • AJAX를 사용한 비동기 로그인 처리
    • 로그인 성공 시 JWT 토큰을 localStorage에 저장
    • 에러 메시지 표시 기능
  2. 대시보드 페이지 (Dashboard.aspx):
    • 인증된 사용자만 접근 가능
    • 사용자 정보 표시
    • 보호된 데이터 로드 기능
    • 로그아웃 기능
  3. 주요 보안 기능:
    • 모든 API 요청에 JWT 토큰 포함
    • 인증되지 않은 접근 시 자동 리다이렉트
    • 토큰 만료 시 자동 로그아웃

구현 시 주의사항:

  1. localStorage 대신 보안이 더 필요한 경우 httpOnly 쿠키 사용을 고려해 볼 수 있습니다.
  2. 실제 운영 환경에서는 HTTPS 사용 필수입니다.
  3. CSRF 공격 방지를 위한 대책을 추가해 주는 게 좋습니다.

이상 JWT에 대하여 간단하게 알아보았습니다.

반응형
반응형

JavaScript의 class 시스템은 ES6(ECMAScript 2015)에서 도입된 문법으로, 객체 지향 프로그래밍(OOP)을 보다 직관적이고 간결하게 표현할 수 있도록 도와줍니다. 

보통 규모있는 프론트엔드 개발을 위해서라면 안 쓸 수가 없는 class에 대하여 대략적인 흐름을 알아봅시다.

1. 클래스 정의

클래스는 class 키워드를 사용하여 정의합니다.

class Person {
  constructor(name, age) {
    this.name = name; // 인스턴스의 name 속성
    this.age = age;   // 인스턴스의 age 속성
  }

  // 메서드 정의
  introduce() {
    console.log(`Hi, I'm ${this.name} and I'm ${this.age} years old.`);
  }
}

// 인스턴스 생성
const person1 = new Person('Alice', 30);
person1.introduce(); // Hi, I'm Alice and I'm 30 years old.

2. 클래스의 주요 구성 요소

(1) 생성자 (Constructor)

  • constructor 메서드는 클래스의 인스턴스를 생성하고 초기화하는 역할을 합니다.
  • 클래스당 하나의 constructor만 가질 수 있습니다.
  • 생략하면 기본 생성자가 자동으로 제공됩니다.

(2) 인스턴스 메서드

  • 클래스 내부에 정의된 메서드는 클래스의 인스턴스에서 사용할 수 있습니다.
  • 메서드 정의 시 function 키워드를 생략합니다.
class Animal {
  constructor(name) {
    this.name = name;
  }

  makeSound() {
    console.log(`${this.name} makes a sound.`);
  }
}

const animal = new Animal('Dog');
animal.makeSound(); // Dog makes a sound.

(3) 정적 메서드 (Static Methods)

  • static 키워드로 정의된 메서드는 클래스 자체에서 호출할 수 있습니다. 인스턴스에서는 호출할 수 없습니다.
class MathUtils {
  static add(a, b) {
    return a + b;
  }
}

console.log(MathUtils.add(5, 3)); // 8

(4) 접근 제한자 (JavaScript 기본)

  • JavaScript에서는 기본적으로 모든 속성과 메서드가 public입니다.
  • 하지만, **프라이빗 필드(private fields)**를 정의할 때 #을 사용할 수 있습니다.
class BankAccount {
  #balance; // private 필드

  constructor(initialBalance) {
    this.#balance = initialBalance;
  }

  deposit(amount) {
    this.#balance += amount;
  }

  getBalance() {
    return this.#balance;
  }
}

const account = new BankAccount(1000);
account.deposit(500);
console.log(account.getBalance()); // 1500
// console.log(account.#balance); // 에러: private 필드는 직접 접근 불가

3. 상속(Inheritance)

extends 키워드를 사용하여 클래스 상속을 구현할 수 있습니다.

class Vehicle {
  constructor(brand) {
    this.brand = brand;
  }

  start() {
    console.log(`${this.brand} is starting.`);
  }
}

class Car extends Vehicle {
  constructor(brand, model) {
    super(brand); // 부모 클래스의 생성자 호출
    this.model = model;
  }

  start() {
    super.start(); // 부모 클래스의 메서드 호출
    console.log(`${this.model} is ready to go!`);
  }
}

const myCar = new Car('Toyota', 'Corolla');
myCar.start();
// Toyota is starting.
// Corolla is ready to go!

4. 클래스의 동적 확장

클래스는 함수처럼 동적으로 정의되며, 실행 중 변경하거나 추가할 수도 있습니다.

class DynamicClass {}
DynamicClass.prototype.greet = function () {
  console.log('Hello from dynamic class!');
};

const instance = new DynamicClass();
instance.greet(); // Hello from dynamic class!

5. 클래스의 특징과 한계

특징:

  • 객체 지향적인 코드를 작성하기 쉽고, 코드 재사용성을 높입니다.
  • 기존의 프로토타입 기반 문법보다 가독성이 좋습니다.
  • ES6 표준으로 대부분의 최신 브라우저와 Node.js에서 지원됩니다.

한계:

  • JavaScript의 클래스는 "진짜 클래스"라기보다는 프로토타입 기반 상속의 Syntax Sugar입니다.
  • 다중 상속을 지원하지 않습니다. (하지만 mixins 같은 패턴으로 해결 가능)
  • private 필드는 한정적인 경우에만 사용 가능하며, 완벽한 데이터 은닉을 보장하지 않습니다.

6. 클래스 사용 사례

  • 사용자 정의 데이터 구조 생성
  • UI 컴포넌트 구현
  • 데이터 처리 및 비즈니스 로직 캡슐화
  • 공통 동작(예: API 호출)을 모듈화

JavaScript의 class는 기존의 객체 지향 언어와 유사한 구문을 제공하며, 개발자에게 친숙하고 효율적인 방식으로 코드를 작성할 수 있게 합니다.

반응형
반응형

JavaScript의 export와 import는 모듈 시스템의 핵심 기능으로, 코드를 여러 파일로 나누어 관리하고 재사용성을 높이는 데 사용됩니다. 혼자서 개발 연습하시다가 실무로 들어가거나 사이즈가 큰 프로젝트를 만들때는 반듯이 필요한 기능이므로 작 숙지하고 넘어가셔야 합니다.


export

export는 특정 값을 다른 파일에서 사용할 수 있도록 내보낼 때 사용합니다. 내보낼 수 있는 항목에는 변수, 함수, 클래스 등이 포함됩니다.

1. Named Export (이름 있는 내보내기)

하나의 파일에서 여러 값을 내보낼 수 있습니다. 이름을 지정해서 내보내기 때문에 가져올 때 같은 이름을 사용해야 합니다.

// utils.js
export const add = (a, b) => a + b;
export const subtract = (a, b) => a - b;
export const multiply = (a, b) => a * b;

2. Default Export (기본 내보내기)

한 파일당 하나의 기본 값을 내보낼 수 있습니다. 이름 없이 내보내며, 가져올 때 원하는 이름으로 사용할 수 있습니다.

// logger.js
export default function log(message) {
  console.log(message);
}

import

import는 다른 파일에서 내보낸 값을 가져올 때 사용합니다.

1. Named Import (이름 있는 가져오기)

내보낼 때 사용된 이름과 동일한 이름으로 가져옵니다. 중괄호 {}를 사용합니다.

// main.js
import { add, subtract } from './utils.js';

console.log(add(2, 3)); // 5
console.log(subtract(5, 3)); // 2

2. Default Import (기본 가져오기)

기본으로 내보낸 값은 중괄호 없이 가져옵니다. 가져올 때 이름을 자유롭게 지정할 수 있습니다.

// main.js
import log from './logger.js';

log('Hello, World!'); // Hello, World!

3. Named Import와 Default Import 함께 사용

이 두 가지 방식을 동시에 사용할 수 있습니다.

// main.js
import log, { add, subtract } from './utils.js';

log('Logging from utils');
console.log(add(1, 2)); // 3

4. Import as (이름 변경)

가져오는 항목의 이름을 변경할 수도 있습니다.

// main.js
import { add as addition } from './utils.js';

console.log(addition(2, 3)); // 5

5. Import All (모두 가져오기)

모듈의 모든 내보낸 항목을 한 객체로 가져옵니다.

// main.js
import * as utils from './utils.js';

console.log(utils.add(2, 3)); // 5
console.log(utils.subtract(5, 3)); // 2

사용 시 주의사항

  1. 파일 경로: import할 때 파일 경로를 정확히 지정해야 합니다.
    • 상대 경로: ./ 또는 ../로 시작
    • 절대 경로: 프로젝트 설정에 따라 다름
  2. 확장자: .js 확장자를 명시하는 것이 권장됩니다.
  3. 동기/비동기: import는 기본적으로 동기적으로 동작하지만, import() 함수를 사용하면 동적으로 비동기 import도 가능합니다.

예제: 모듈 파일 분리와 사용

utils.js

export const greet = (name) => `Hello, ${name}!`;
export const farewell = (name) => `Goodbye, ${name}!`;

app.js

import { greet, farewell } from './utils.js';

console.log(greet('Alice')); // Hello, Alice!
console.log(farewell('Alice')); // Goodbye, Alice!

이처럼 export와 import를 사용하면 코드 구조를 모듈화하여 관리하기 쉽고 재사용성이 높아집니다.

 

도움이 되셨으면 좋겠습니다.

반응형

+ Recent posts