프론트와 백 이해하기 1 (프로젝트 생성 과정)

심영민·2025년 3월 6일
0

유레카

목록 보기
19/33

배운게 너무 많아서 어디서부터 정리할지 감이 안잡히는중....

이번 수업에서 스프링부트,SQL,VSC를 이용해 쇼핑몰의 한 페이지를 구현해보았다. (로그인, 회원가입, 상품 등록 까지)

우선 이번 포스팅에서는 간단히, 수업 내용 복습겸 상품들을 어떻게 SQL에서 불러오고(GET) 회원가입 정보는 어떻게 저장되어(POST) 동작하는지를 정리해보겠다.

필요한 툴은 MYSQL, SpringBoot, VScode 이다.

1. MYSQL에서


우선 SQL에서 프로젝트에 필요한 member 테이블과 product 테이블을 생성해줘야한다.

위 그림에서 보이는 멤버 테이블의 행은 프로젝트에서 내가 회원가입한 데이터들이 저장된 모습이다.

2. VSCODE에서

vscode에서는 Front 폴더에 index.html과 index.js와 같이 프론트에서 보이는 정적인 파일들을 작성한다.

index.js는 다음과 같은 동작을 포함시켰다.

window.onload = async () => {
  // 페이지 로드 시 실행될 비동기 함수

  // 세션 스토리지에서 이메일 정보 가져오기
  const email = sessionStorage.getItem("email");
  if (email) {
    // 이메일 정보가 있다면 로그인 상태 표시
    document.getElementById("loginSpan").innerHTML =
      email + ` <button id="logout">logout</button>`;
  }
  // axios.defaults.withCredentials = true; // 쿠키를 포함할지 여부 설정
  console.log(axios); // axios 객체 출력

  // 모든 상품 정보 가져오기 (fetch API 사용)
  let productList = await fetch("getAllProducts", { method: "GET" });
  console.log(productList); // 응답 객체 출력
  productList = await productList.json(); // 응답 객체를 JSON 형식으로 변환
  console.log(productList); // 상품 정보 출력

  // 상품 목록을 표시할 HTML 생성
  let productListDiv = ``;
  productList.forEach((item) => {
    productListDiv += `<div class="card m-3" style="width: 10rem;">
                        <img src="img/${item.pimg}" class="card-img-top" alt="...">
                        <div class="card-body">
                          <b class="card-title">${item.prodname}</b>
                          <p class="card-text text-danger">${item.price}원</p>
                          <a href="#" class="btn btn-outline-info" id="addCart">장바구니 담기</a>
                        </div>
                      </div>`;
  });
  document.getElementById("productListDiv").innerHTML = productListDiv; // 상품 목록 HTML을 페이지에 추가
};

// 회원가입 버튼 클릭 이벤트 핸들러
document.getElementById("signupBtn").addEventListener("click", async () => {
  // 입력 필드에서 값 가져오기
  const nickname = document.getElementById("nickname").value;
  const email = document.getElementById("email").value;
  const pwd = document.getElementById("pwd").value;
  const data = { nickname, email, pwd }; // 데이터 객체 생성

  // 회원가입 요청 보내기 (fetch API 사용)
  let response = await fetch("insertMember", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
    },
    body: JSON.stringify(data), // 데이터를 JSON 형식으로 변환하여 요청 본문에 추가
  });
  response = await response.json(); // 응답 객체를 JSON 형식으로 변환
  console.log(response); // 응답 객체 출력

  if (response.msg === "ok") {
    console.log("ok"); // 회원가입 성공 메시지 출력
    const modal = bootstrap.Modal.getInstance(
      document.getElementById("signupModal")
    ); // 회원가입 모달 가져오기
    modal.hide(); // 회원가입 모달 숨기기
    document.getElementById("signupLi").remove(); // 회원가입 메뉴 항목 제거
  } else {
    alert(response.msg); // 회원가입 실패 메시지 출력
  }
});

// 로그인 버튼 클릭 이벤트 핸들러
document.getElementById("loginBtn").addEventListener("click", async () => {
  // 입력 필드에서 값 가져오기
  const email = document.getElementById("loginEmail").value;
  const pwd = document.getElementById("loginPwd").value;
  const data = { email, pwd }; // 데이터 객체 생성

  // 로그인 요청 보내기 (axios 사용)
  let response = await axios.post("login", data);
  console.log(response); // 응답 객체 출력
  alert(response.data.msg); // 로그인 결과 메시지 출력

  if (response.data.msg === "ok") {
    const modal = bootstrap.Modal.getInstance(
      document.getElementById("loginModal")
    ); // 로그인 모달 가져오기
    modal.hide(); // 로그인 모달 숨기기
    document.getElementById("loginSpan").innerHTML =
      email + ` <button id="logout">logout</button>`; // 로그인 상태 표시
    window.sessionStorage.setItem("email", email); // 세션 스토리지에 이메일 정보 저장
  }
});

// 상품 목록에서 '장바구니 담기' 버튼 클릭 이벤트 핸들러
document.getElementById("productListDiv").addEventListener("click", (event) => {
  if (event.target.id == "addCart") {
    axios.post("addCart", {}); // 장바구니 담기 요청 보내기 (axios 사용)
  }
});

// 로그아웃 버튼 클릭 이벤트 핸들러
document.getElementById("loginSpan").addEventListener("click", (event) => {
  if (event.target.id == "logout") {
    sessionStorage.removeItem("email"); // 세션 스토리지에서 이메일 정보 제거
    window.location.reload(); // 페이지 새로고침
  }
});

로그인 시 axios 사용 이유

  • POST 요청 & JSON 데이터:
    • 로그인은 정보 전송이 필요 → POST 요청이 일반적
    • 로그인 정보는 JSON 형식으로 주고받는 경우가 많음
    • axios는 POST 요청 + JSON 데이터 처리 간편함
  • 세밀한 오류 처리:
    • 로그인 실패 (잘못된 비밀번호 등) → HTTP 오류 발생 가능
    • axios는 HTTP 오류도 catch로 잡아 처리 용이!
    • fetch는 네트워크 오류만 reject 처리
  • 쿠키 & 세션 관리:
    • 로그인 성공 후 → 서버와 사용자 상태 유지 필요
    • 쿠키나 세션 정보를 주고받아야 함!
    • axios는 withCredentials 옵션을 true로 주어 교차 출처 요청할 때 쿠키 자동 포함 가능
  • 요청/응답 인터셉터:
    • 로그인 시 토큰을 넣거나 응답에 공통 처리 필요
    • axios는 요청/응답 가로채기 가능 → 유연한 처리!
  • 기타 편의 기능:
    • 자동 JSON 변환: fetchresponse.json() 필요
    • XSRF 보호: 보안 기능 내장
    • 구형 브라우저에서도 사용가능

결론:

  • 로그인은 복잡한 HTTP 요청 → axios가 유리함
  • 다양한 기능 + 편리한 API → 개발 효율 ↑

3. 스프링부트에서

이제 서버쪽 관련 코드를 위해 스프링 스타터 프로젝트를 생성해줘야한다.

추가할 의존성은 그림과 같이 5가지이다.

  • Spring Boot DevTools: 개발 편의성을 높여주는 도구 (자동 재시작, 라이브 리로드 등)
  • Spring Web: 웹 애플리케이션 개발에 필요한 기본적인 의존성 (Spring MVC, REST API 등)
  • MyBatis Framework: SQL Mapper 프레임워크 (SQL과 Java 객체 매핑해주어 오류가 덜 난다고한다..)
  • H2 Database: 경량 인메모리 데이터베이스
    (컴퓨터 안에서 데이터를 임시로 저장하고, 필요할 때 빠르게 꺼내 쓸 수 있도록 도와주는 미니 데이터 저장소)
  • MySQL Driver: MySQL 데이터베이스 연결 드라이버

전체 폴더 구조


위 폴더를 보면 Project1_Cafe2가 내가 생성한 프로젝트이다.

폴더의 구조는 크게 src/main/javasrc/main/resources가 있는데

  • src/main/java는 애플리케이션의 주요 자바파일들을 저장하는 폴더이고
  • src/main/resources는 애플리케이션 설정 파일이나 외부 정적 리소스 파일 등이 저장된다.

파일들은 다음을 수행한다.

src/main/java

  • com.shop.cafe:
    • 애플리케이션의 핵심 비즈니스 로직과 관련된 코드들이 위치하는 최상위 패키지.
  • ProjectCafe2Application.java:
    • 스프링 부트 애플리케이션의 진입점(Entry Point).
    • @SpringBootApplication 어노테이션을 통해 스프링 부트 자동 설정 활성화 및 컴포넌트 스캔 시작.
    • main() 메서드에서 SpringApplication.run() 호출하여 내장 WAS(Web Application Server) 구동 및 애플리케이션 컨텍스트 초기화.
    • @PropertySource 어노테이션을 사용하여 외부 설정 파일 (secu.properties) 로드 및 애플리케이션 프로퍼티 분리 관리.
  • controller:
    • 클라이언트로부터의 HTTP 요청을 처리하고 응답을 반환하는 컨트롤러 클래스들을 포함.
    • @Controller 또는 @RestController 어노테이션을 사용하여 스프링 MVC 컨트롤러로 등록.
    • URL 매핑을 통해 특정 요청을 처리하는 핸들러 메서드 정의.
    • 클라이언트 요청 데이터 검증 및 서비스 계층 호출하여 비즈니스 로직 처리.
  • dao (Data Access Object):
    • 데이터베이스와의 상호작용을 담당하는 데이터 접근 객체 클래스들을 포함.
    • 데이터베이스 연결, 쿼리 실행, 결과 매핑 등 데이터베이스 관련 작업을 수행.
    • MyBatis 또는 JPA와 같은 ORM(Object-Relational Mapping) 프레임워크를 사용하여 데이터베이스 접근 추상화 및 코드 간소화.
  • dto (Data Transfer Object):
    • 계층 간 데이터 교환을 위한 데이터 전송 객체 클래스들을 포함.
    • 데이터베이스 엔티티와 클라이언트 요청/응답 데이터 간의 매핑을 담당.
    • 필요한 데이터 필드 및 검증 규칙을 정의하여 데이터 무결성 보장.
  • service:
    • 애플리케이션의 핵심 비즈니스 로직을 구현하는 서비스 클래스들을 포함.
    • 컨트롤러와 DAO 사이에서 비즈니스 규칙 적용, 트랜잭션 관리, 예외 처리 등의 역할을 수행.
    • @Service 어노테이션을 사용하여 스프링 빈으로 등록 및 의존성 주입을 통해 DAO 또는 다른 서비스 컴포넌트 사용.

src/main/resources

  • application.properties:
    • 애플리케이션 전반에 걸친 설정을 정의하는 파일.
    • 데이터베이스 연결 정보, 서버 포트, 로깅 설정, 외부 설정 파일 경로 등 다양한 프로퍼티를 설정.
    • 외부 설정 (환경 변수 등) 을 활용하여 민감한 정보 (비밀번호 등) 를 안전하게 관리.
  • config:
    • secu.properties 와 같은 별도의 설정 파일을 포함하여 보안 관련 설정을 분리.
    • 데이터베이스 접근 계정 정보, API 키 등 민감한 정보를 외부 설정 파일에 분리하여 소스 코드에서 분리 및 보안 강화.
  • static:
    • 클라이언트 측 코드 (HTML, CSS, JavaScript) 및 정적 리소스 파일들을 포함.
    • VS Code와 같은 외부 에디터에서 개발된 프론트엔드 코드를 스프링 부트 애플리케이션 내에 포함하여 실행 가능.
    • 정적 리소스는 웹 서버에 의해 직접 제공되며, 클라이언트 측에서 처리되는 동적 로직에 사용.

전체 프로젝트 구조를 살펴보았으니 다음 포스팅에는 세션과 쿠키, 전체 서버 아키텍처 등에 대해 자세히 정리해보겠다!

profile
코딩너무어려운대 어떡할과 재학중

0개의 댓글