back-end/Spring

(Spring Boot)회원관리

doheun 2023. 4. 11. 17:27
반응형

Spring Boot

회원관리

프로젝트시 기본 템플릿

application.properties

# server port설정
server.port=8070

# jsp를 사용할 경우 설정-추가적으로 의존성 추가
spring.mvc.view.prefix=/view/
spring.mvc.view.suffix=.jsp

# thymeleaf사용 여부
spring.thymeleaf.enabled=true

# jsp와 thymeleaf동시에 사용하는 경우 구분을 짓기 위해 설정
spring.thymeleaf.view-names=thymeleaf/*

spring.thymeleaf.prefix=classpath:/templates/
spring.thymeleaf.suffix=.html

# cache활성화 여부, 개발환경시에는 비활성화
spring.thymeleaf.cache=false

#template인코딩
spring.thymeleaf.encoding=UTF-8
spring.thymeleaf.mode=HTML

#렌더링전에 template 존재 여부확인
spring.thymeleaf.check-template=true

#template위치 존재 여부 확인
spring.thymeleaf.check-template-location=true

# DB설정
spring.datasource.driver-class-name=org.mariadb.jdbc.Driver
spring.datasource.url=jdbc:mariadb://localhost:3306/hk
spring.datasource.username=root
spring.datasource.password=1234

# mybatis설정
mybatis.type-aliases-package=com.example.demo.dtos
mybatis.mapper-locations=classpath:/mybatis/**/*.xml

흐름

  1. DB설계

    CREATE TABLE member (
    `memberid` INT NOT NULL AUTO_INCREMENT,
        `id` VARCHAR(40) NOT NULL ,
        `name` VARCHAR(40) NOT NULL, 
    `password` VARCHAR(50) NOT NULL unique, 
    `email` VARCHAR(50) NOT NULL unique,
    `address` VARCHAR(1000) NOT NULL,
    `role` VARCHAR(15) NOT NULL  ,
    PRIMARY KEY (`memberid`) 
    );
  2. DTO

MemeberDto.java

@Getter
@Setter
@ToString
@NoArgsConstructor
@AllArgsConstructor
@Alias(value="MemberDto")
public class MemberDto {
    private int memberId;
    private String id;
    private String name;
    private String password;
    private String email;
    private String address;
    private String role;
}
  1. memberMapper.xml에서 쿼리
  2. DAO 인터페이스로 쿼리와 매핑 (스프링부트 특징)
    • @Mapper를 통해 sql쿼리 mapper와 Dao인터페이스를 매핑
    • 기존 스프링에서는 @Repository사용했음

MemberMapper.java

@Mapper
public interface MemberMapper {
    //추가
    public boolean addUser(MemberDto dto);
    //로그인
    public MemberDto loginUser(String id);
    //아이디 체크
    public String idChk(String id);

}

memberMapper.xml

<mapper namespace="com.example.demo.mapper.MemberMapper">
    <insert id="addUser" parameterType="MemberDto">
        INSERT INTO MEMBER VALUES(NULL,#{id},#{name}
        ,#{password},#{email},#{address},'USER')
    </insert>

    <select id="idChk" parameterType="String" resultType="String">
        SELECT ID FROM MEMBER WHERE ID=#{id}
    </select>

    <select id="loginUser" parameterType="String" resultType="MemberDto">
        SELECT MEMBERID,ID,NAME,PASSWORD,EMAIL,ADDRESS,ROLE 
        FROM MEMBER
        WHERE ID=#{id}
    </select>
</mapper>
  1. service클래스 생성(MemberService.java)
    • command를 사용해서 화면으로부터 입력되는 값을 command클래스에서 받아서 전달
    • com.example.demo.command.AddUserCommand
    • 값을 담아서 client- service로 운반 (기존 DB- service)
    • DTO에서는 @getter,setter로 command에서는 @data로 구분
    • 유효성 처리
    • service에서 DTO객체생성하여 command로부터 받아서 set
    • password는 암호화하여 따로 처리 (security)
    • idchk

MemberService.java

@Service  
public class MemberService {


@Autowired
private    MemberMapper memberMapper;

public boolean addUser(AddUserCommand addUserCommand) {

    MemberDto mdto=new MemberDto();
    mdto.setId(addUserCommand.getId());
    mdto.setName(addUserCommand.getName());
    mdto.setPassword(addUserCommand.getPassword());
    mdto.setEmail(addUserCommand.getEmail());
    mdto.setAddress(addUserCommand.getAddress());

    return memberMapper.addUser(mdto);
}
public String idChk(String id) {

    return memberMapper.idChk(id);
}

AddUserCommand.java

@Data  
public class AddUserCommand {  
@NotBlank(message = "ID입력 필수")  
private String id;  
@NotBlank(message = "이름입력 필수")  
private String name;  
@NotBlank(message = "비밀번호 입력 필수")  
@Length(min = 8,max =16,message = "비밀번호는 8~16이하로 입력")  
private String password;  
@NotBlank(message = "이메일 입력 필수")  
private String email;  
@NotBlank(message = "주소 입력 필수")  
private String address;  
}
  1. thymeleaf/member/addUserForm.html
  2. HomeController -> MemeberController를 이용해서 RequsetMapper(value="/user")로 회원가입폼으로 이동하는 경로를 바꿀수 있음

MemberController.java

@Controller
@RequestMapping(value="/user")
public class MemberController {

    @Autowired
    private MemberService MemberService;

    @GetMapping(value="/addUserForm")
    public String addUserForm(Model model) {
        System.out.println("회원가입 폼이동");
        model.addAttribute("addUserCommand",new AddUserCommand());
        return "thymeleaf/member/addUserForm";
    }

    @PostMapping(value="/addUser")
    public String addUser(@Validated AddUserCommand command,
            BindingResult result,Model model) {

        if(result.hasErrors()) {
            return "thymeleaf/member/addUserForm";
        }

        try {
            boolean isS=MemberService.addUser(command);
            System.out.println("회원가입성공");
            return "redirect:/home";
        } catch (Exception e) {
            System.out.println("회원가입실패");
            e.printStackTrace();
            return "redirect:addUserForm";
        }

    }
}
  1. 회원가입 화면구성 (form action="user/addUser" method="post")
  2. memberController에서 만들어놓은command클래스를 통해 화면으로부터 전달 받는다

오류
(conn=2023) Column 'id' cannot be null
; (conn=2023) Column 'id' cannot be null

addUserForm.html에서
th:field="*{id}" 각각 붙이기


오류
컨트롤러로부터 command객체를 받아서 페이지 전체 수정 필요-> 컨트롤러로 이동해서 파라미터 추가
회원가입 폼으로 이동할 때 빈 객체를 같이 보내서 받아서 저장후에 다시 받는다

해결-> service에서 따로 처리 안함....

  1. thymeleaf에서 제공하는 include방식 사용해서 UI템플릿 적용

layouts-layout.html,fragments-header,footer.html
부트스트랩,jquery관련은 layout파일 상단에 적용

<html xmlns:th="http://www.thymeleaf.org"
      xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout">

<body>
    <div th:replace="fragmens/header::header"></div>

    <div layout:fragment="content" class="content"></div>

    <div th:replace="fragmens/footer::footer"></div>
</body>

header,footer에 기존 템플릿 적용 후
layout.html 적용을 위해 dependency 적용

    <dependency>
          <groupId>nz.net.ultraq.thymeleaf</groupId>
          <artifactId>thymeleaf-layout-dialect</artifactId>
      </dependency>

layout.html을 사용하려는 페이지에 각각의 html태그 정의후

<html xmlns:th="http://www.thymeleaf.org"
    xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
    layout:decorate="~{layouts/layout}">
<div layout:fragment="content" class="content"></div>

위의 태그로 body태그의내용을 class를 제외하고 감싸준다


오류 화면처리
해결 : header와 footer에 fragments처리 안함

  1. 아이디 ajax처리
    컨트롤러에서 @GetMapping, @ResponseBody를 통해 map형태 반환
    의존성 추가 (json관련)addUserForm.html에서 jquery를 이용해 id ajax
    ajax처리시 각각의 필요한 속성, data에대한 것 확실히 공부
<dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> </dependency> <!-- @ResponseBody사용 --> <dependency> <groupId>org.codehaus.jackson</groupId> <artifactId>jackson-mapper-asl</artifactId> <version>1.9.13</version> </dependency> <!-- jsonView 사용 --> <dependency> <groupId>net.sf.json-lib</groupId> <artifactId>json-lib</artifactId> <version>2.3</version> <classifier>jdk15</classifier> </dependency>
  1. 비밀번호처리(암호화) -Security참고
    의존성 추가암호화 적용후 화면
    id: user
    password : c24c0723-96f8-4246-b98f-a37f4f447a89
    <!-- 암호화 -->
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
    </dependency>
    

스프링 security 환경 설정가능(따로 클래스 생성)

memberService.java-> password암호화(개발자도 모름)

com.example.demo.config.SecurityConfig.java에서 객체로 설정한 것 @Autowired로 불러서
비밀번호 인코딩

mdto.setPassword(passwordEncoder.encode(addUserCommand.getPassword()));

---
오류
암호화할 떄 기본 비밀번호보다 길이 길어짐
데이터베이스 비밀번호 VARCHAR(100)으로 변경
해결

14. 로그인 구현-DB정보와 일치한지 확인
command로 클래스 만들어서 구현
com.example.demo.command.LoginCommand.java
- @Data
- 유효성체크(@NotBlank)
- service에서 LoginCommand의 정보,HttpServletRequest로 세션을 받아서 처리
- service에서 화면 흐름처리 같이 있기 때문에 controller와 조정 필요

public String loginUser(LoginCommand loginCommand,
HttpServletRequest request) {
System.out.println("로그인");
String path="home";
MemberDto mdto=memberMapper.loginUser(loginCommand.getId());
if(mdto !=null) {//존재시
//(앞,뒤)앞: 화면으로부터 입력 ,뒤: DB
if(passwordEncoder.matches(loginCommand.getPassword(), mdto.getPassword())) {
System.out.println("패스워드 비교: 일치");
request.getSession().setAttribute("mdto", mdto);
return path;
}else {
System.out.println("패스워드 틀림");
path="thymeleaf/member/login";//로그인페이지 이동
}
}else {
System.out.println("회원이 아님");
path="thymeleaf/member/login";
}
return path;
}

15. login.html(layout적용)
- form태그 thymeleaf이용 (메세지 전달을 위해 객체에 담아서 )
- 페이지의 th:object에 담은 객체가 이상이 없다면 컨트롤러에서 정상 작동 하지만 이상이 있다면 따로 th:if로 처리해준 error처리(span태그)
- input 부트스트랩 처리 class="form-control"
- 컨트롤러에서 로그인 폼으로 넘어가는 처리


16. 컨트롤러
- loginform에서 객체를 받아 @Validated , BindingResult
- command파라미터 다음에 BindResult선언
- command유효값처리 결과가 BindingResult에서 받아짐
- loginForm,login처리(HttpServletRequest 사용이유)

17. home.html에서 session 여부 처리

HOME

SignIn SignUp

[[${session.mdto.id}]] 로그아웃

``` 18. 로그아웃 처리 ``` @GetMapping("/logout") public String logout(HttpServletRequest request) { System.out.println("로그아웃"); request.getSession().invalidate(); return "redirect:/home"; } ```
  1. 게시판 기능 시작
    mybatis/boardMapper.xml
    com.example.demo.mapper.BoardMapper.java(DAO인터페이스)생성
    테이블생성

Security

com.example.demo.config.SecurityConfig.java

//security사용할 때 환경설정 필요
@Configuration //Bean을 등록하기 위해 붙여주는 어노테이션으로 , 설정한다는 의미
@EnableWebSecurity
public class SecurityConfig {

    //security 사용할 때 환경설정 필요
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http.cors().disable()
            .csrf().disable()
            .formLogin().disable() //시큐리티 기본 로그인 폼 사용 안함
            .headers().frameOptions().disable();
        return http.build();
    }


    //패스워드 암호화 해줄 객체
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}

Thymeleaf

기본기능

th:text="${}"

text를 html에 출력할 때 사용

<td th:text="${dto.id}"></td>
[[${dto.id}]] 태그 내부에 사용하지 않고 바로 출력할 경우
th:href="@{url}"

a태그에서 사용

//파라미터를 넘길때
<a th:href="@{/board/boardlist(pnum=${dto.pnum})}"></a>
th:value="${}"

input 태그에서 value값 지정

form 요소

th:action="@{}"

form태그 사용시 ,해당 경로로 요청을 보낼 떄 사용

th:object="${}"

form submit할 때, th:object에 설정된 객체가 전달, 서버로부터 올때도 th:object에 설정해준 객체로 받아진다

th:field="*{}"

각각 필드들을 매핑해주는 역할, th:object에 설정해준 객체의 내부와 매칭
-> @Validated를 사용하여 유효성 검사할 때 사용될 수 있는데 form안에 데이터들을 object로 선언한 객체에 담아서 컨트롤러로 전달하게 되고 @Validated로 선언하여 받고, 유효성 검사를 하고 결과를 BindingResult에서 확인가능

조건문

th:if="${}"

<span th:if="${fields.hasErrors('id')" th:errors="*{id}"></span>

기타참고

  1. 스프링 부트 유효성처리
    만약 화면에서 null값으로 올때 유효값을 처리함에있어서 관리를 용이하게 하기 위해 스프링 부트에서 따로 처리하는 기능이 있음
    기존에 스프링에서는 값을 처리할 때 여기저기 코드들이 흩어져 있기 떄문에 보기 불편
    validation의존성 추가
  • @NotBlank : String타입에 주로 사용하고 아무것도 입력을 안했을시에 메세지 남길 수 있음
  • @Length : 최소,최대 길이,message처리가능
  1. 스프링 레거시와 스프링 부트의 큰 차이
  • 설정의 간소화
  • 내장 서버
  • 의존성 관리
  • 자동구성
  • 운영환경설정
  1. css

/* 첫 번째 요소가

태그이고, 바로 다음에 나오는 요소가 태그일 때 스타일을 적용 /
p + a {
/
스타일 */
}
```

  1. 렌더링
    웹 애플리케이션에서 사용자에게 보여지는 화면(HTML, XML, JSON 등)을 생성하는 과정을 의미합니다. 렌더링은 서버에서 클라이언트로 화면을 동적으로 생성하거나, 데이터를 가공하여 웹 페이지나 API 응답을 생성하는 작업을 말합니다.
    서버 사이드 렌더링(Server-Side Rendering, SSR):
    서버에서 데이터와 뷰 템플릿을 결합하여 클라이언트에게 완전한 HTML 페이지를 전달하는 방식입니다. 서버에서 데이터를 가공하여 뷰 템플릿에 적용하고, 최종적으로 클라이언트에게 완성된 HTML을 전송하여 화면이 렌더링됩니다. 스프링 부트에서는 Thymeleaf, JSP, Freemarker 등의 템플릿 엔진을 사용하여 서버 사이드 렌더링을 구현할 수 있습니다.

클라이언트 사이드 렌더링(Client-Side Rendering, CSR):
서버는 데이터만 제공하고, 클라이언트에서 데이터를 가지고 화면을 동적으로 생성하는 방식입니다. 클라이언트에서 JavaScript를 사용하여 데이터를 가공하고, DOM(Document Object Model) 조작을 통해 화면을 렌더링합니다. 스프링 부트에서는 React, Angular, Vue 등의 JavaScript 프레임워크나 라이브러리를 사용하여 클라이언트 사이드 렌더링을 구현할 수 있습니다.

  1. jsp -> 서버사이드렌더링
    하나를 거쳐서 진행하기 때문에 UI템플릿 thymeleaf를 사용
    다른 UI프레임워크와 연동할 떄 html로 정해진 thymeleaf를 사용하는 것이 안정적
  2. src/test/java ->단위테스트 작성
  3. 패키징 war -> 웹환경(배포 구조)
  4. 스프링부트 pom.xml에서 버전이 빠짐
  5. DTO를 빈으로 등록해서 command에서 사용가능?
    원래 autowired하려면 xml에서 빈으로 등록하거나 component-scan을 사용했지만
  • @Bean으로 스프링에서 객체로 인식하도록 할 수 있는 기능도 있음
  • @Component해주고 @Autowired로 사용가능
  1. (jquery) keyup?
    jQuery에서 .on("keyup", ...) 함수는 HTML DOM(Document Object Model)에 있는 하나 이상의 요소의 "keyup" 이벤트에 함수를 바인딩하는 이벤트 핸들러입니다.

"keyup" 이벤트는 키보드의 키를 눌렀다가 떼면 트리거됩니다. .on("keyup", ...) 함수를 사용하면 선택한 요소에서 이 이벤트가 발생할 때마다 실행될 함수를 지정할 수 있습니다.

예를 들어 다음 코드는 ID가 "myInput"인 입력 요소의 "keyup" 이벤트에 함수를 바인딩합니다.

// Bind the keyup event handler to an input element with the ID "myInput"
$("#myInput").on("keyup", function(event) {
  // Get the value of the input field
  var inputValue = $(this).val();

  // Make an AJAX request using jQuery's $.ajax() function
  $.ajax({
    url: "example.com/some_api_endpoint",
    type: "GET",
    data: { input: inputValue },
    success: function(response) {
      // Code to be executed when the AJAX request is successful
      console.log("Response:", response);
    },
    error: function(jqXHR, textStatus, errorThrown) {
      // Code to be executed when the AJAX request encounters an error
      console.log("Error:", textStatus, errorThrown);
    }
  });
});

이 예제에서 keyup 이벤트 핸들러는 ID가 "myInput"인 입력 요소에 바인딩됩니다. 해당 입력 필드에서 키를 놓을 때마다 이벤트 핸들러 기능이 트리거됩니다. 이벤트 핸들러 내에서 $(this).val()을 사용하여 입력 필드의 값을 검색한 다음 해당 값을 AJAX 요청의 데이터로 사용할 수 있습니다. AJAX 요청은 jQuery의 $.ajax() 함수를 사용하여 이루어지며 URL, 유형(예: GET, POST), 데이터, 성공 콜백(성공적인 응답 처리) 및 오류 콜백(처리)을 지정할 수 있습니다. 오류 응답)을 매개변수로 사용합니다.

  1. Security
    Spring Boot에서 HttpSecurity는 Spring Security를 사용하는 애플리케이션에 대한 보안 설정을 구성하는 데 사용되는 클래스입니다. 다음은 Spring Boot 애플리케이션에서 HttpSecurity를 사용하는 단계입니다.
반응형

'back-end > Spring' 카테고리의 다른 글

(Spring Boot)회원관리,게시판,파일업로드  (1) 2023.04.13
(Spring Boot)세팅  (0) 2023.04.11