BACKEND/스프링 Spring

Spring Framework 회원 프로젝트

꾸준히개발하자 2024. 1. 3. 17:54

개발환경

  • IntelliJ IDEA Community Edition
  • amazon corretto open jdk 11
  • mysql 8
  • mybatis
  • JSP
  • Tomcat 9

주요 기능

  • 회원가입
  • 로그인
  • 회원목록 출력
  • 상세조회
  • 수정
  • 삭제
  • ajax 이메일 중복체크

기존 기본 설정 

 

 

인텔리제이 , 톰켓 서버 연결 

 

servlet-context.xml

 

<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/mvc"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xmlns:beans="http://www.springframework.org/schema/beans"
             xmlns:context="http://www.springframework.org/schema/context"
             xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
		http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <annotation-driven />

    <!-- Handles HTTP GET requests for /resources/** by efficiently serving up static resources in the ${webappRoot}/resources directory -->
    <resources mapping="/resources/**" location="/resources/"/>

    <!-- Resolves views selected for rendering by @Controllers to .jsp resources in the /WEB-INF/views directory -->
    <beans:bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <beans:property name="prefix" value="/WEB-INF/views/" />
        <beans:property name="suffix" value=".jsp" />
    </beans:bean>

    <!-- 프로젝트 패키지이름 확인 -->
    <context:component-scan base-package="com.codingrecipe.member" />

</beans:beans>

 

 

 

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
         version="3.1">

  <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>
      /WEB-INF/root-context.xml
    </param-value>
  </context-param>

  <listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>

  <servlet>
    <servlet-name>appServlet</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>/WEB-INF/servlet-context.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>

  <servlet-mapping>
    <servlet-name>appServlet</servlet-name>
    <url-pattern>/</url-pattern>
  </servlet-mapping>

  <filter>
    <filter-name>encodingFilter</filter-name>
    <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
    <init-param>
      <param-name>encoding</param-name>
      <param-value>UTF-8</param-value>
    </init-param>
  </filter>

  <filter-mapping>
    <filter-name>encodingFilter</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>

</web-app>

 

 

pom.xml

 

<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>org.example</groupId>
  <artifactId>member_framework</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>war</packaging>

  <name>member_framework Maven Webapp</name>
  <!-- FIXME change it to the project's website -->
  <url>http://www.example.com</url>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>11</maven.compiler.source>
    <maven.compiler.target>11</maven.compiler.target>
    <java-version>11</java-version>
    <org.springframework-version>5.3.20</org.springframework-version>
    <org.slf4j-version>1.7.25</org.slf4j-version>
  </properties>

  <dependencies>
    <!-- Spring -->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>${org.springframework-version}</version>
      <exclusions>
        <!-- Exclude Commons Logging in favor of SLF4j -->
        <exclusion>
          <groupId>commons-logging</groupId>
          <artifactId>commons-logging</artifactId>
        </exclusion>
      </exclusions>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>${org.springframework-version}</version>
    </dependency>

    <!-- Logging -->
    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-api</artifactId>
      <version>${org.slf4j-version}</version>
    </dependency>
    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>jcl-over-slf4j</artifactId>
      <version>${org.slf4j-version}</version>
      <scope>runtime</scope>
    </dependency>

    <!-- @Inject -->
    <dependency>
      <groupId>javax.inject</groupId>
      <artifactId>javax.inject</artifactId>
      <version>1</version>
    </dependency>

    <!-- javax.servlet-api -->
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>javax.servlet-api</artifactId>
      <version>3.1.0</version>
      <scope>provided</scope>
    </dependency>
    <dependency>
      <groupId>javax.servlet.jsp</groupId>
      <artifactId>jsp-api</artifactId>
      <version>2.1</version>
      <scope>provided</scope>
    </dependency>
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>jstl</artifactId>
      <version>1.2</version>
    </dependency>

    <!-- data base -->
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>8.0.22</version>
    </dependency>
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis</artifactId>
      <version>3.5.6</version>
    </dependency>
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis-spring</artifactId>
      <version>1.3.2</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-jdbc</artifactId>
      <version>${org.springframework-version}</version>
    </dependency>
    <dependency>
      <groupId>org.apache.tomcat</groupId>
      <artifactId>tomcat-dbcp</artifactId>
      <version>9.0.31</version>
    </dependency>

    <!-- fileupload -->
    <dependency>
      <groupId>commons-fileupload</groupId>
      <artifactId>commons-fileupload</artifactId>
      <version>1.3.1</version>
    </dependency>

    <!-- Lombok -->
    <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
      <version>1.18.12</version>
      <scope>provided</scope>
    </dependency>

    <!-- json -->
    <dependency>
      <groupId>com.googlecode.json-simple</groupId>
      <artifactId>json-simple</artifactId>
      <version>1.1.1</version>
    </dependency>
    <dependency>
      <groupId>com.google.code.gson</groupId>
      <artifactId>gson</artifactId>
      <version>2.8.2</version>
    </dependency>
    <!-- jackson-core -->
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-core</artifactId>
      <version>2.9.2</version>
    </dependency>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
      <scope>test</scope>
    </dependency>
  </dependencies>

  <build>
    <finalName>mvc_test</finalName>
    <pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
      <plugins>
        <plugin>
          <artifactId>maven-clean-plugin</artifactId>
          <version>3.1.0</version>
        </plugin>
        <!-- see http://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_war_packaging -->
        <plugin>
          <artifactId>maven-resources-plugin</artifactId>
          <version>3.0.2</version>
        </plugin>
        <plugin>
          <artifactId>maven-compiler-plugin</artifactId>
          <version>3.8.0</version>
        </plugin>
        <plugin>
          <artifactId>maven-surefire-plugin</artifactId>
          <version>2.22.1</version>
        </plugin>
        <plugin>
          <artifactId>maven-war-plugin</artifactId>
          <version>3.2.2</version>
        </plugin>
        <plugin>
          <artifactId>maven-install-plugin</artifactId>
          <version>2.5.2</version>
        </plugin>
        <plugin>
          <artifactId>maven-deploy-plugin</artifactId>
          <version>2.8.2</version>
        </plugin>
      </plugins>
    </pluginManagement>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <configuration>
          <source>11</source>
          <target>11</target>
        </configuration>
      </plugin>
    </plugins>
  </build>
</project>

 

 

DB sql

-- 계정 생성하기
create database db_codingrecipe;
create user user_codingrecipe@localhost identified by '1234';
grant all privileges on db_codingrecipe.* to user_codingrecipe@localhost;

-- 회원 테이블
drop table if exists member_table;
create table member_table(
	id bigint primary key auto_increment,
    memberEmail varchar(20) unique,
    memberPassword varchar(20),
    memberName varchar(20),
    memberAge int,
    memberMobile varchar(30)
);

데이터 베이스 생성 쿼리문 

회원 테이블 생성 쿼리문 

 

 

 

DB 계정과 회원 테이블을 생성 합니다. 

툴은 miria DB 를 사용하였습니다.

 

회원 가입 기능 

 

 

index.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>index</title>
</head>
<body>
    <h2>hello spring framework 회원 프로젝트 입니다.</h2>
    <a href="/member/save">회원가입</a>
    <a href="/member/login">로그인</a>
    <a href="/member/">회원목록 조회</a>
</body>
</html>

 

MemberController 

package com.codingrecipe.member.controller;

import com.codingrecipe.member.dto.MemberDTO;
import com.codingrecipe.member.service.MemberService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpSession;
import java.util.List;

@Controller
@RequestMapping("/member")        // 공통 주소를 표현 그 이하의 주소를 메서드로 받아준다. 
@RequiredArgsConstructor
public class MemberController {
    private final MemberService memberService;

//  @GetMapping("/member/save")   // /member/member/save
    @GetMapping("/save")
    public String saveForm() {    
        return "save";
    }

}

 

 

save.jsp - 회원 가입 페이지 

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>save</title>
    <script src="https://code.jquery.com/jquery-3.6.3.min.js" integrity="sha256-pvPw+upLPUjgMXY0G+8O0xUf+/Im1MZjXxxgOcBQBXU=" crossorigin="anonymous"></script>
</head>
<body>
    <form action="/member/save" method="post">
        <input type="text" name="memberEmail" placeholder="이메일" id="memberEmail" onblur="emailCheck()">
        <p id="check-result"></p>
        <input type="text" name="memberPassword" placeholder="비밀번호">
        <input type="text" name="memberName" placeholder="이름">
        <input type="text" name="memberAge" placeholder="나이">
        <input type="text" name="memberMobile" placeholder="전화번호">
        <input type="submit" value="회원가입">
    </form>
</body>
</script>
</html>

 

/member/save 로 post 방식으로 

이름 비밀번호 전화번호 등 5가지 항목으로 보낸다.

회원가입 버튼을 누르면 submit 실행 되서 action url 이동 

 

 

MemberDTO

import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

@Getter
@Setter
@ToString
public class MemberDTO {
    private Long id;			// 아이디
    private String memberEmail; // 이메일
    private String memberPassword;  // 패스워드
    private String memberName;      // 이름
    private int memberAge;          // 나이
    private String memberMobile;    // 번호 
}

 

클래스를 만든다

회원관련된 정보를 정의 한다.

회원가입을 받아줄때 전달하는 역할 + 조회정보를 화면에 보여주는 용도 

회원과 관련된 모든 정보를 표현해줘야 한다. 

필드명은 폼에서 전송하는 name 과 반드시 일치 해야한다. 

대소문자가 틀리면 값이 담아 질수가 없다.

 

 

MemberController

package com.codingrecipe.member.controller;

import com.codingrecipe.member.dto.MemberDTO;
import com.codingrecipe.member.service.MemberService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpSession;
import java.util.List;

@Controller
@RequestMapping("/member")        // 공통 주소를 표현 그 이하의 주소를 메서드로 받아준다. 
@RequiredArgsConstructor
public class MemberController {
    private final MemberService memberService;

//  @GetMapping("/member/save")   // /member/member/save
    @GetMapping("/save")
    public String saveForm() {    
        return "save";
    }
	
	@PostMapping("/save")
    public String save(@ModelAttribute MemberDTO memberDTO) {
        int saveResult = memberService.save(memberDTO);
        if (saveResult > 0) {
            return "login";
        } else {
            return "save";
        }
    }
}

 

주소 요청하는 controller 주소를 만들려 가보자

회원가입 정보를 받아주는 메소드

DTO에 담겨있는 값은 DB가 된다. 

멤버 서비스 클래스를 의존성 주입해서 활용할수 있도록 한다. 

리턴값에 따라 가입 성공 실패 여부를 확인 할 수 있다.

 

 

memberService 

package com.codingrecipe.member.service;

import com.codingrecipe.member.dto.MemberDTO;
import com.codingrecipe.member.repository.MemberRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
@RequiredArgsConstructor
public class MemberService {
    private final MemberRepository memberRepository;

    public int save(MemberDTO memberDTO) {
        return memberRepository.save(memberDTO);
    }
}

@RequiredArgsConstructor 롬복 어노테이션을 사용

final MemberRepository 에서 

final 이 붙은 필드를 가진 생성자를 만든다.  생성자 주입 이라고 표현 한다.

 

 

Repository 클래스 

package com.codingrecipe.member.repository;

import com.codingrecipe.member.dto.MemberDTO;
import lombok.RequiredArgsConstructor;
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.stereotype.Repository;

import java.util.List;

@Repository
@RequiredArgsConstructor
public class MemberRepository {
    private final SqlSessionTemplate sql;
    public int save(MemberDTO memberDTO) {
        System.out.println("memberDTO = " + memberDTO);
        return sql.insert("Member.save", memberDTO);
    }
}

DB와 연관된 클래스 , 마이바티스에 기능별로 쿼리 정리 적절한 쿼리들을 호출해주고 매개변수가 있다면 넘겨주면 리턴값이 있으면 리턴해주는 주된 역할을 해준다 

매개변수로 넘겨주고 있는 값이 전달되어 있는지 확인 

 

마이바티스에서 제공하는 SqlSessionTemplate 객체로 접근해서

CRUD 를 위한 메서드를 제공해준다.  

 

DB에 값을 넣는 insert 를 사용한다. 

 

 

root-context.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:jdbc="http://www.springframework.org/schema/jdbc"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc.xsd
		http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">

    <!-- 데이터베이스 이름 및 계정 확인 -->
    <bean id="dataSource"
          class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/db_codingrecipe?useSSL=false&amp;serverTimezone=Asia/Seoul" />
        <property name="username" value="user_codingrecipe"/>
        <property name="password" value="1234"/>
    </bean>

    <!-- 파일 모두 생성했는지 확인 Mapper 에 대한 위치를 지정한다. (위치 참조) -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <property name="configLocation" value ="classpath:/mybatis-config.xml" />
        <property name="mapperLocations" value="classpath:/mapper/*.xml" />
    </bean>

    <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
        <constructor-arg name="sqlSessionFactory" ref="sqlSessionFactory" />
    </bean>

    <bean id="transactionManager"
          class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource" />
    </bean>

    <bean id="multipartResolver"
          class="org.springframework.web.multipart.commons.CommonsMultipartResolver">

        <property name="defaultEncoding" value="UTF-8" />
        <property name="maxUploadSize" value="10000000" />

    </bean>

    <tx:annotation-driven transaction-manager="transactionManager"/>

</beans>

 

DB와 관련된 세팅 , 파일업로드(게시판할때 쓸것입니다) 코드 

 <property name="url" value="jdbc:mysql://localhost:3306/db_codingrecipe?useSSL=false&serverTimezone=Asia/Seoul" /> 테이블 스페이스 이름 을 지정 

계정과 정보를 입력한다. 

이러한 정보들이 스프링 빈으로 등록되면서 서로 간에 연계되면서 서로를 호출되서 등록 된다.

Repository 에서 Mybatis를 이용해서 db쿼리를 보낼수 있게 된다. 

 

 

resources 폴더 - Mybatis-config.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <typeAliases>
        <typeAlias type="com.codingrecipe.member.dto.MemberDTO" alias="member"></typeAlias>
    </typeAliases>
</configuration>

 

 

 

resources 폴더 - mapper 폴더 - memberMapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="Member">
    <insert id="save" parameterType="member">
        insert into member_table(memberEmail, memberPassword, memberName, memberAge, memberMobile)
        values (#{memberEmail}, #{memberPassword}, #{memberName}, #{memberAge}, #{memberMobile})
    </insert>
</mapper>

 

마이바티스 사용 회원가입 처리를 위한 쿼리문 

namespace 이름 Member 와 sql.insert("Member.save",memberDTO) 호출하면서

id 는 Member.save 의 save를 호출한다. 

 

즉 namespace = Member , id = save 이다.

parameterType 은 com.codingrecipe.member.dto.MemberDTO 풀경로를 적는데,

번거롭고 길기 때문에 약간의 줄임 형태로 member로 줄이려면

 

mybatis-config.xml 에서 알리아스 설정에 별칭을 지정하는 설정을 해줘서

alias로 적용한 member 로 사용이 가능하다. 

 

네이티브 쿼리를 사용하며 테이블 이름 각 컬럼 값을 쓰게 된다.

각 위치의 데이터베이스 필드이름 과 클래스 필드명을 #{필드명} 로 표현해준다.

 

 

-로그인 기능 

login.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>login</title>
</head>
<body>
    <form action="/member/login" method="post">
        <input type="text" name="memberEmail" placeholder="이메일">
        <input type="text" name="memberPassword" placeholder="비밀번호">
        <input type="submit" value="로그인">
    </form>
</body>
</html>

 

로그인은 이메일 , 비밀번호를 적어서 일치하면 main 

틀렸으면 로그인 페이지가 다시 나오게 끔 한다.

 

 

MemberController - login메소드  로그인 처리를 하는 메서드 

 @PostMapping("/login")
    public String login(@ModelAttribute MemberDTO memberDTO,
                        HttpSession session) {
        boolean loginResult = memberService.login(memberDTO);
        if (loginResult) {
            session.setAttribute("loginEmail", memberDTO.getMemberEmail());
            return "main";
        } else {
            return "login";
        }
    }

로그인을 하게 되면 Session 활용 

매개변수에 HttpSession 줘서 로그인을 성공하게 되면 setAttribute 로 저장 한다.

세션에다가 로그인한 계정 정보를 입력을 하도록 한다.

그다음 main.jsp 로 넘어가고

실패시 login 페이지로 간다

 

 

MemberService

   public boolean login(MemberDTO memberDTO) {
        MemberDTO loginMember = memberRepository.login(memberDTO);
        if (loginMember != null) {
            return true;
        } else {
            return false;
        }
    }

 

login 한 결과를 DTO로 받아온다

그 결과에 null 여부에 따라 true (로그인 성공) , false (결과 없음) 리턴 

 

Repository

public MemberDTO login(MemberDTO memberDTO) {
        return sql.selectOne("Member.login", memberDTO);
    }

 

sql.selectOne은 selectOne은 조회결과가 하나 일때 

MemberDTO는 로그인한 부분을 호출 DTO객체를 넘겨준다.

조회결과가 여러개면 ? 500에러 발생

 

<select id="login" parameterType="member" resultType="member">
        select * from member_table where memberEmail=#{memberEmail} and memberPassword=#{memberPassword}
    </select>

 

member에 대한 정보를 줘서 쿼리문 이메일 과 패스워드를 조회 한 결과값을 member로 반환 한다.

넘겨받은 이메일과 패스워드를 둘다 만족하는 결과가 있는지 select 한다 

사용자가 회원가입 할때 이메일과 비번이 둘다 일치하는지 

 

select 할때는 resultType 이 있어야 한다. 

 

 

Main.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>main</title>
</head>
<body>
    <h2>${sessionScope.loginEmail} 님 환영합니다.</h2>
    <button onclick="update()">내정보 수정하기</button>
    <button onclick="logout()">로그아웃</button>

</body>
<script>
    const update = () => {
        location.href = "/member/update";
    }
    const logout = () => {
        location.href = "/member/logout";
    }
</script>
</html>

세션에 담긴 이메일 띄어주고 

수정을 위한 버튼 로그인을 위한 버튼을 만들어 놨다

 

 

-- 회원 목록 조회 기능

@GetMapping("/")
    public String findAll(Model model) {
        List<MemberDTO> memberDTOList = memberService.findAll();
        model.addAttribute("memberList", memberDTOList);
        return "list";
    }

 

데이터를 가져가야 하는 Model 객체 필요

 

 

memberService 클래스

public List<MemberDTO> findAll() {
        return memberRepository.findAll();
    }

 

 

memberRepository 클래스

public List<MemberDTO> findAll() {
        return sql.selectList("Member.findAll");
    }

 

sql.selectList 값이 여러개인 경우 

 

memberMapper.xml

 <select id="findAll" resultType="member">
        select * from member_table
    </select>

 

전달받은 값이 없으므로 ParameterType은 없고 

결과값인 ResultType 이 필요

-> MySql에서 실행해보고 마이바티스에 적용하는게 좋다 쿼리문 문제되는 경우 500에러가 발생 한다.

 

 

list.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
    <title>list</title>
</head>
<body>
    <table>
        <tr>
            <th>id</th>
            <th>email</th>
            <th>password</th>
            <th>name</th>
            <th>age</th>
            <th>mobile</th>
            <th>조회</th>
            <th>삭제</th>
        </tr>
        <c:forEach items="${memberList}" var="member">
            <tr>
                <td>${member.id}</td>
                <td>

                    <a href="/member?id=${member.id}">${member.memberEmail}</a>
                </td>
                <td>${member.memberPassword}</td>
                <td>${member.memberName}</td>
                <td>${member.memberAge}</td>
                <td>${member.memberMobile}</td>
                <td>
                    <a href="/member?id=${member.id}">조회</a>
                </td>
                <td>
                    <button onclick="deleteMember('${member.id}')">삭제</button>
                </td>
            </tr>
        </c:forEach>
    </table>
</body>
<script>
    const deleteMember = (id) => {
        console.log(id);
        location.href = "/member/delete?id="+id;
    }
</script>
</html>

 

JSTL 의 반복문을 사용하기 위해

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> 선언해준다. 

 

이제 조회 시 아이디 값을 해당하는 정보를 가지고 화면에 보여주기 

// /member?id=1
    @GetMapping
    public String findById(@RequestParam("id") Long id, Model model) {
        MemberDTO memberDTO = memberService.findById(id);
        model.addAttribute("member", memberDTO);
        return "detail";
    }

 

 

Service 클래스

public MemberDTO findById(Long id) {
        return memberRepository.findById(id);
    }

 

나중에 잘못된 아이디 요청이 올수도 있으니 해당 아이디 일치하는 화면이 없다 라듯이

기능을 보강할때 고민해보면 좋을것 같다.

 

 

Repository 클래스

public MemberDTO findById(Long id) {
        return sql.selectOne("Member.findById", id);
    }

 

 

Membermapper.xml

<select id="findById" parameterType="Long" resultType="member">
        select * from member_table where id=#{id}
    </select>

ParameterType : id  - Long 타입

ResultType : member 

 

 

detail.jsp - 회원 정보를 뿌려 준다.

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>detail</title>
</head>
<body>
    <table>
        <tr>
            <th>id</th>
            <td>${member.id}</td>
        </tr>
        <tr>
            <th>email</th>
            <td>${member.memberEmail}</td>
        </tr>
        <tr>
            <th>password</th>
            <td>${member.memberPassword}</td>
        </tr>
        <tr>
            <th>name</th>
            <td>${member.memberName}</td>
        </tr>
        <tr>
            <th>age</th>
            <td>${member.memberAge}</td>
        </tr>
        <tr>
            <th>mobile</th>
            <td>${member.memberMobile}</td>
        </tr>
    </table>
</body>
</html>

 

Controller 에서 아이디 정보를 가져와서 조회된 결과값을 JSP 에 뿌려준다 

 

 

- 삭제 기능 

<button onclick="deleteMember('${member.id}')">삭제</button>

삭제 버튼 클릭 시 전달하는 값을 싱글쿼터로 묶는게 중요하다. 

 

<script>
    const deleteMember = (id) => {
        console.log(id);
        location.href = "/member/delete?id="+id;
    }
</script>

id값이 넘어와서 주소값으로 넘기게 된다.

 

 

@GetMapping("/delete")
    public String delete(@RequestParam("id") Long id) {
        memberService.delete(id);
        return "redirect:/member/";
    }

 

 

Controller 

@GetMapping("/delete")
    public String delete(@RequestParam("id") Long id) {
        memberService.delete(id);
        return "redirect:/member/";
    }

삭제 처리를 수행하고 그 다음 리스트를 재요청 하는 형식 

redirect 방식 재 요청 

 

Service 클래스

public void delete(Long id) {
    memberRepository.delete(id);
}

 

Repository 클래스

public void delete(Long id) {
        sql.delete("Member.delete", id);
    }

 

 

mapper.xml

<delete id="delete" parameterType="Long">
        delete from member_table where id=#{id}
    </delete>

쿼리문 작성해 준다.

delete 는 int 타입으로 반환해서 삭제가 되면 1 삭제가 안되면 0 으로 반환한다. 

삭제가 됬냐 안됐냐 판단이 가능하다.

 

-- 수정 기능 (2번의 요청이 발생한다)

1. 수정을 요청하면 수정 요청 해서 수정을 위한 화면을 띄어준다.

2. 수정 화면에서 전화번호 와 나이 값을 수정을 하게 한다.

3. 값을 db에 반영 시킨다.

4. 수정이 끝나면 회원의 상세 화면을 띄어준다.

 

<body>
    <h2>${sessionScope.loginEmail} 님 환영합니다.</h2>
    <button onclick="update()">내정보 수정하기</button>
    <button onclick="logout()">로그아웃</button>

</body>
<script>
    const update = () => {
        location.href = "/member/update";
    }
    const logout = () => {
        location.href = "/member/logout";
    }
</script>

 

수정을 위한 페이지를 띄어 준다.

 // 수정화면 요청
    @GetMapping("/update")
    public String updateForm(HttpSession session, Model model) {
        // 세션에 저장된 나의 이메일 가져오기
        String loginEmail = (String) session.getAttribute("loginEmail");
        MemberDTO memberDTO = memberService.findByMemberEmail(loginEmail);
        model.addAttribute("member", memberDTO);
        return "update";
    }

 

주소를 요청해서 왔는데, 누구의 정보를 수정할것인지 알아야 되므로

HttpSession 에다가 내 로그인 정보를 담겨있으므로 이것을 활용해본다

 

 (String) session.getAttribute("loginEmail")  Object를 String으로 형변환 해서 가져온다. 

 

 

memberService 클래스

public MemberDTO findByMemberEmail(String loginEmail) {
    return memberRepository.findByMemberEmail(loginEmail);
}

이메일을 조회 한다.

 

Repository 클래스

public MemberDTO findByMemberEmail(String loginEmail) {
        return sql.selectOne("Member.findByMemberEmail", loginEmail);
    }

 

Membermapper.xml

<select id="findByMemberEmail" parameterType="String" resultType="member">
        select * from member_table where memberEmail=#{loginEmail}
    </select>

 

Controller 에서 조회된 결과를 update.jsp로 넘긴다.

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>update</title>
</head>
<body>
    <form action="/member/update" method="post" name="updateForm">
        id: <input type="text" name="id" value="${member.id}"readonly>
        email: <input type="text" name="memberEmail" value="${member.memberEmail}"readonly>
        password: <input type="text" name="memberPassword" id="memberPassword">
        name: <input type="text" name="memberName" value="${member.memberName}" readonly>
        age: <input type="text" name="memberAge" value="${member.memberAge}">
        mobile: <input type="text" name="memberMobile" value="${member.memberMobile}">
        <input type="button" value="수정" onclick="update()">

    </form>
</div>
</body>
<script>
    const update = () => {
        const passwordDB = '${member.memberPassword}';
        const password = document.getElementById("memberPassword").value;
        if (passwordDB == password) {
            document.updateForm.submit();
        } else {
            alert("비밀번호가 일치하지 않습니다!");
        }
    }
</script>
</html>

 

readonly 읽기 전용 으로 수정할수 없는 부분은 readonly 으로 설정해 준다.

값은 value 라는 속성으로 controller 에서 member라는 이름으로 member라는 파라미터로 각각의 필드를 찍어주고있다

 

input type 이 submit 이 아니라 button 

submit 으로 하면 form 태그가 제출되는데

button은 다른 기능을 주고 싶을때 사용한다.

 

form 태그 안에서 button을 줘서 함수를 호출 한다. - >  onclick = "update()" 호출 

 

<script>
    const update = () => {
        const passwordDB = '${member.memberPassword}';
        const password = document.getElementById("memberPassword").value;
        if (passwordDB == password) {
            document.updateForm.submit();
        } else {
            alert("비밀번호가 일치하지 않습니다!");
        }
    }
</script>

 

DB에 저장된 비밀번호 랑 사용자가 입력한 비밀번호를 비교 한다.

사용자가 입력한 비밀번호를 DOM API 를 이용해서 getElementById로 가져온다

일치하면 document.updateForm.submit()  Form 태그를 제어한다.

 

-- 수정 처리 (PostMapping)

 // 수정 처리
    @PostMapping("/update")
    public String update(@ModelAttribute MemberDTO memberDTO) {
        boolean result = memberService.update(memberDTO);
        if (result) {
            return "redirect:/member?id=" + memberDTO.getId();
        } else {
            return "index";
        }
    }

수정이 끝나면 회원의 상세페이지로 이동시킨다. 

수정 실패시 index.jsp로 이동 

 

memberSerivce

public boolean update(MemberDTO memberDTO) {
    int result = memberRepository.update(memberDTO);
    if (result > 0) {
        return true;
    } else {
        return false;
    }
}

update 결과 값 수정이 완료되면 1 , 실패시 0 반환 

 

 

Repository

public int update(MemberDTO memberDTO) {
        return sql.update("Member.update", memberDTO);
    }

 

mapper.xml

<update id="update" parameterType="member">
    update member_table set memberAge=#{memberAge}, memberMobile=#{memberMobile}
    where id=#{id}
</update>

 

여러개의 컬럼값을 수정할때는 and (x) , 콤마를 준다

수정 처리 과정은 2번의 과정이 필요하다.

 

 

이메일 중복 체크 - Ajax 사용 프론트에서 리액트 등을 사용 할 수 있다.

같이 사용이 가능한 문법 이다. 

 

DB에 동일한 주소가 있는지 체크 

 

save.jsp

 

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>save</title>
    <script src="https://code.jquery.com/jquery-3.6.3.min.js" integrity="sha256-pvPw+upLPUjgMXY0G+8O0xUf+/Im1MZjXxxgOcBQBXU=" crossorigin="anonymous"></script>
</head>
<body>
    <form action="/member/save" method="post">
        <input type="text" name="memberEmail" placeholder="이메일" id="memberEmail" onblur="emailCheck()">
        <p id="check-result"></p>
        <input type="text" name="memberPassword" placeholder="비밀번호">
        <input type="text" name="memberName" placeholder="이름">
        <input type="text" name="memberAge" placeholder="나이">
        <input type="text" name="memberMobile" placeholder="전화번호">
        <input type="submit" value="회원가입">
    </form>
</body>

 

onblur : 이벤트 처리하는 방법 , 입력창을 벗어났을때 이 함수를 호출하는 이벤트 처리 방식

 

<script>
// 이메일 입력값을 가져오고,
// 입력값을 서버로 전송하고 똑같은 이메일이 있는지 체크한 후
// 사용 가능 여부를 이메일 입력창 아래에 표시
    const emailCheck = () => {
        const email = document.getElementById("memberEmail").value;
        const checkResult = document.getElementById("check-result");
        console.log("입력한 이메일", email);
        $.ajax({
            // 요청방식: post, url: "email-check", 데이터: 이메일
            type: "post",
            url: "/member/email-check",
            data: {
                "memberEmail": email
            },
            success: function(res) {
                console.log("요청성공", res);
                if (res == "ok") {
                    console.log("사용가능한 이메일");
                    checkResult.style.color = "green";
                    checkResult.innerHTML = "사용가능한 이메일";
                } else {
                    console.log("이미 사용중인 이메일");
                    checkResult.style.color = "red";
                    checkResult.innerHTML = "이미 사용중인 이메일";
                }
            },
            error: function(err) {
                console.log("에러발생", err);
            }
        });
    }
</script>

 

ajax 는 화면이 바뀌는게 없이 서버와 HTTP 요청을 주고받을때 

비동기 통신을 사용하는데

get 또는 post 요청 방식이 필요하고 post 방식으로 url 주소로 보내주고

보내주고 하는 내용은 data : { 파라미터이름 : 담기는 값 } - 사용자가 입력한 이메일 값이 된다.

 

success : 요청이 성공하면 응답이 오게 되고 응답에 대해서 처리 한다.

function(res) 

res 에  ok 값이 담겨오면 db에 없는 이메일이다 - 사용 가능

 

error : function(err)  응답이 제대로 오지 않는 경우 

 

@PostMapping("/email-check")
    public @ResponseBody String emailCheck(@RequestParam("memberEmail") String memberEmail) {
        System.out.println("memberEmail = " + memberEmail);
        String checkResult = memberService.emailCheck(memberEmail);
        return checkResult;
    }

 

post 방식으로 요청을 보내면 postMapping 으로 받는다

Ajax나 응답을 처리할때 ResponseBody 로 응답을 받는다

String 타입으로 받는데 @ResponseBody 로 꼭 받는다.

 

MemberService

public String emailCheck(String memberEmail) {
        MemberDTO memberDTO = memberRepository.findByMemberEmail(memberEmail);
        if (memberDTO == null) {
            return "ok";
        } else {
            return "no";
        }
    }

 

res값으로 결과에 대한 응답값을 리턴받는다.

조회결과가 있으면 그 이메일을 사용하는 사용자가 있다. 

없으면 그 이메일을 사용하는 사용자가 없다 

 

 

Repository 

 public MemberDTO findByMemberEmail(String loginEmail) {
        return sql.selectOne("Member.findByMemberEmail", loginEmail);
    }

 

membermapper.xml

<select id="findByMemberEmail" parameterType="String" resultType="member">
        select * from member_table where memberEmail=#{loginEmail}
    </select>

 

500 에러 발생시 -> 매퍼에서 문제가 발생할 확률이 높다