일기장 토이 프로젝트

LeeKyoungChang·2022년 2월 18일
0
post-thumbnail

📚 프로젝트 초기 설정, 시작하기 앞서

프로젝트 초기 설정 해줘야 한다. (블로그 초기 설정 참고하기)
css 추가(css 추가는 자료 찾아보면 금방 나온다.)
스크린샷 2022-02-18 오후 2 50 40

 

📖 A. domain

domain/member/Member

package project.mydailytp.domain.member;

import lombok.Data;

import javax.validation.constraints.NotEmpty;

// 로그인할 때 사용하는 클래스
@Data
public class Member {

    // id 지정 (DB 번호)
    private Long id;

    @NotEmpty
    private String nickname;  // 사용자 이름

    @NotEmpty
    private String loginId;  // 로그인 ID

    @NotEmpty
    private String password;  // 비밀번호

}

 

domain/member/MemberRepository

package project.mydailytp.domain.member;

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Repository;

import java.util.*;

@Slf4j
@Repository
public class MemberRepository {

    // Member : id, email, password, nickname
    private static Map<Long, Member> store = new HashMap<>();
    private static long sequence = 0L; // member가 추가될 때마다 +1 된다.

    // 저장하기 위해서 호출한 메서드
    public Member save(Member member) {
        member.setId(++sequence);
        store.put(member.getId(), member);
        return member;
    }

    // id를 찾습니다.
    public Member findById(Long id) {
        return store.get(id);
    }

    // 로그인을 시도한 id가 회원가입한 id인지 확인하기 위해 호출한 메서드
    public Optional<Member> findByLoginId(String loginId) {
        return findAll().stream()
                .filter(m -> m.getLoginId().equals(loginId))
                .findFirst();
    }

    // 저장소에서 값들을 모두 꺼내서 ArrayList에 담는다.
    public List<Member> findAll() {
        return new ArrayList<>(store.values());
    }

    // 저장소를 비운다.
    public void clearStore() {
        store.clear();
    }
}

 

domain/login/LoginService

package project.mydailytp.domain.login;

import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import project.mydailytp.domain.member.Member;
import project.mydailytp.domain.member.MemberRepository;

@Service
@RequiredArgsConstructor  // final이 붙은 멤버변수만 사용해서 생성자를 자동으로 만들어준다.
public class LoginService {

    private final MemberRepository memberRepository;

    public Member login(String loginId, String password) {
        return memberRepository.findByLoginId(loginId)
                .filter(m -> m.getPassword().equals(password))
                .orElse(null);
    }
}

 

domain/mypage/MyPage

package project.mydailytp.domain.mypage;

import lombok.Data;

@Data
public class MyPage {


    private Long id;  // 번호

    private String name;
    private int birthday;  // 생년월일
    private String school;  // 학교
    private String position; // 기술스택
    private String book;  // 책
    private String game; // 게임
    private String study; // 공부
    private String blog; // 블로그


    public MyPage() {
    }

    public MyPage(String name, int birthday, String school, String position, String book, String game, String study, String blog) {
        this.name = name;
        this.birthday = birthday;
        this.school = school;
        this.position = position;
        this.book = book;
        this.game = game;
        this.study = study;
        this.blog = blog;
    }
}

 

domain/mypage/MyPageRepository

package project.mydailytp.domain.mypage;

import org.springframework.stereotype.Repository;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Repository
public class MyPageRepository {

    // id와 Item요소들을 저장하기 위한 변수
    private static final Map<Long, MyPage> store = new HashMap<>();
    private static long sequence = 0L; // Long의 초기값

    // 값을 저장하기 위한 메소드
    public MyPage save(MyPage item) {
        item.setId(++sequence);
        store.put(item.getId(), item);
        return item;
    }


    // 값을 반환하기 위해 생성한 메소드
    public MyPage findById(Long id){
        return store.get(id);
    }

    // 저장되어 있는 값들을 ArrayList로 반환해준다.
    public List<MyPage> findAll() {
        return new ArrayList<>(store.values());
    }

    private String name;
    private Long birthday;  // 생년월일
    private String school;  // 학교
    private String position; // 기술스택

    // 업데이트
    public void update(Long itemId, MyPage updateParam) {
        MyPage findItem = findById(itemId);
        findItem.setName(updateParam.getName());
        findItem.setBirthday(updateParam.getBirthday());
        findItem.setSchool(updateParam.getSchool());
        findItem.setPosition(updateParam.getPosition());
        findItem.setBook(updateParam.getBook());
        findItem.setBlog(updateParam.getBlog());
        findItem.setGame(updateParam.getGame());
        findItem.setStudy(updateParam.getStudy());
    }

    // 전체적으로 정리
    public void clearStore(){
        store.clear();
    }
}

📖 B. WebConfig, TestData

WebConfig

package project.mydailytp;


@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new LogInterceptor())
                .order(1)
                .addPathPatterns("/**")
                .excludePathPatterns("/css/**", "/*.ico", "/error");

        registry.addInterceptor(new LoginCheckInterceptor())
                .order(2)
                .addPathPatterns("/**")
                .excludePathPatterns("/" ,"/members/add",
                        "/login", "/logout", "/css/**", "/*.ico", "/error");
    }
}

 

TestDataInit

package project.mydailytp;

import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
import project.mydailytp.domain.member.Member;
import project.mydailytp.domain.member.MemberRepository;
import project.mydailytp.domain.mypage.MyPageRepository;

import javax.annotation.PostConstruct;

@Component
@RequiredArgsConstructor
public class TestDataInit {

    private final MyPageRepository myPageRepository;
    private final MemberRepository memberRepository;

    @PostConstruct
    public void init() {
        Member member = new Member();
        member.setNickname("테스트");
        member.setLoginId("test");
        member.setPassword("test!");

        memberRepository.save(member);
    }
}

 

이제부터는 web 이다.

📚 1. 홈 화면

스크린샷 2022-02-18 오후 2 44 16

 

✔️ 실행된 부분
MydailytpApplication

스크린샷 2022-02-18 오후 2 49 01
  • Application이 실행된다.

 

HomeController

스크린샷 2022-02-18 오후 2 50 00
  • localhost:8080을 실행하였을 때
  • mainHome.html 화면을 띄워준다.

 

mainHome.html

스크린샷 2022-02-18 오후 3 11 23
  • 나의 페이지, 사진 페이지, 달력 페이지가 띄워진다.
  • 페이지 버튼을 클릭 시 해당 페이지로 이동한다. (현재는 나의 페이지만 실행된다.)

 

📚 2. 로그인

나의 페이지 사진 페이지 달력 페이지를 눌렸을 때 로그인이 되어있지 않다면, 로그인 화면이 띄워진다.
로그인 화면 과정 : 로그인 화면 → 회원가입 → 홈 화면 → 로그인 화면 → 나의 페이지

📖 A. 로그인 화면

html에서 @{/mypage}를 클릭했을 경우 localhost:8080/mypage로 이동한다.

mypage구현한 쪽은?
다만, 아직 로그인이 인증이 되지 않아 인터셉터는 적절하지 않은 요청이라 판단, 컨트롤러 호출하지 않는다. (컨트롤러 예외 발생)

스크린샷 2022-02-18 오후 3 30 02
  • excludePathPatterns을 보면 인터셉터를 적용하지 않을 부분에 /mypage가 없다.
  • 그럼?
스크린샷 2022-02-18 오후 3 31 24
  • 현재 세션이 null이다.
  • 미인증 사용자 요청이라 판단하여, /login?redirectURL=로 이동한다.

 

✔️ 로그인 화면
위에서 /login?redirectURL=로 이동한다고 했으니

스크린샷 2022-02-18 오후 3 41 39
  • loginForm메소드를 통해 template/login/loginForm이 실행된다.

 

template/login/loginForm

스크린샷 2022-02-18 오후 3 41 24 스크린샷 2022-02-18 오후 2 44 30
  • 회원가입 페이지를 누를 시, localhost:8080/members/add로 이동하고
  • 로그인을 클릭시 @PostMapping("/login")이 실행된다.
  • 취소 버튼시 메인으로 돌아간다.

 

📚 3. 회원 가입 화면

✔️ 회원가입 페이지 눌렀을 때

MemberController
스크린샷 2022-02-18 오후 3 47 01

  • localhost:8080/members/add가 실행된다.

 

members/addMemberForm.html

<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
  <meta charset="utf-8">
  <link th:href="@{/css/bootstrap.min.css}"
        href="../css/bootstrap.min.css" rel="stylesheet">
  <style>
        .container {
            max-width: 560px;
        }
        .field-error {
            border-color: #dc3545;
            color: #dc3545;
        }
    </style>
</head>
<body>

<div class="container">

  <div class="py-5 text-center">
    <h2>회원 가입</h2>
  </div>

  <form action="" th:action th:object="${member}" method="post">

    <div th:if="${#fields.hasGlobalErrors()}">
      <p class="field-error" th:each="err : ${#fields.globalErrors()}" th:text="${err}">전체 오류 메시지</p>
    </div>

    <div>
      <label for="nickname">이름</label>
      <input type="text" id="nickname" th:field="*{nickname}" class="form-control"
             th:errorclass="field-error">
      <div class="field-error" th:errors="*{nickname}" />
    </div>
    <div>
      <label for="loginId">아이디</label>
      <input type="text" id="loginId" th:field="*{loginId}" class="form-control"
             th:errorclass="field-error">
      <div class="field-error" th:errors="*{loginId}" />
    </div>
    <div>
      <label for="password">비밀번호</label>
      <input type="password" id="password" th:field="*{password}" class="form-control"
             th:errorclass="field-error">
      <div class="field-error" th:errors="*{password}" />
    </div>


    <hr class="my-4">

    <div class="row">
      <div class="col">
        <button class="w-100 btn btn-primary btn-lg" type="submit">회원 가입</button>
      </div>
      <div class="col">
        <button class="w-100 btn btn-secondary btn-lg" onclick="location.href='items.html'" th:onclick="|location.href='@{/login}'|"
                type="button">취소</button>
      </div>
    </div>

  </form>


</div> <!-- /container -->
</body>
</html>

 

스크린샷 2022-02-18 오후 2 44 50
  • 이제 화면에 나온 것을 입력한다.
  • 취소 버튼을 눌리시 localhost:8080/login으로 돌아간다.
  • 회원 가입 버튼을 눌렀을 시 밑의 save 메소드가 실행된다.

 

MemberController/save

스크린샷 2022-02-18 오후 3 47 08
  • 검증에서 오류가 발생할시 다시 회원가입 화면에 띄워진다.
  • 아니라면 memberRepository에 회원 정보를 저장하고, root: localhost:8080으로 이동한다.

 

✔️ 홈 화면으로 이동함
스크린샷 2022-02-18 오후 2 44 57

 

✔️ 회원가입한 아이디, 패스워드 입력

로그인 폼이 실행되며
스크린샷 2022-02-18 오후 3 56 09

 

login/loginForm이 실행된다.

<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="utf-8">
    <link th:href="@{/css/bootstrap.min.css}"
          href="../css/bootstrap.min.css" rel="stylesheet">
    <style>
        .container {
            max-width: 560px;
        }
        .field-error {
            border-color: #dc3545;
            color: #dc3545;
        }
    </style>
</head>
<body>
<div class="container">
    <div class="py-5 text-center">
        <h2>로그인</h2>
    </div>
    <form action="item.html" th:action th:object="${loginForm}" method="post">
        <div th:if="${#fields.hasGlobalErrors()}">
            <p class="field-error" th:each="err : ${#fields.globalErrors()}" th:text="${err}">전체 오류 메시지</p>
        </div>
        <div>
            <label for="loginId">로그인 ID</label>
            <input type="text" id="loginId" th:field="*{loginId}" class="form-control" th:errorclass="field-error">
            <div class="field-error" th:errors="*{loginId}" />
        </div>
        <div>
            <label for="password">비밀번호</label>
            <input type="password" id="password" th:field="*{password}" class="form-control" th:errorclass="field-error">
            <div class="field-error" th:errors="*{password}" />
        </div>
        <hr class="my-4">
        <div class="row">
            <div class="col">
                <button class="w-100 btn btn-secondary btn-lg" type="button" th:onclick="|location.href='@{/members/add}'|">회원가입</button>
            </div>
            <div class="col">
                <button class="w-100 btn btn-primary btn-lg" type="submit">로그인</button>
            </div>
            <div class="col">
                <button class="w-100 btn btn-secondary btn-lg" onclick="location.href='items.html'" th:onclick="|location.href='@{/}'|" type="button">취소</button>
            </div>
        </div>
    </form>
</div> <!-- /container -->
</body>
</html>`

 

idpassword를 입력한다.
스크린샷 2022-02-18 오후 2 45 07

 

loginForm에서 로그인을 클릭했을 때 submit이 실행되어 입력된 loginFormpost형식으로 전달된다.

스크린샷 2022-02-18 오후 5 47 54
  • 검증오류 발생시 다시 login/loginForm으로 이동한다.
  • 입력한 loginIdPasswordMember에 발급받아, loginService에서 체크한다.
스크린샷 2022-02-18 오후 4 03 47

loginid를 찾고, 입력한 password가 맞다면 해당 값을 return해준다.
아니라면 null을 반환해준다. (아닐시 다시 login/loginForm으로 이동한다.)

 

        // 로그인 성공했다면
        // 세션이 있으면 있는 세션을 반환, 없으면 신규 세션을 반환
        HttpSession session = request.getSession();
        // 세션에 로그인 회원 정보 보관
        session.setAttribute(SessionConst.LOGIN_MEMBER, loginmember);

        return "redirect:"+redirectURL;

이제 마지막으로 로그인에 성공했다면

  • 세션이 있으면 해당 세션을 반환받는다.
  • 없으면 신규 세션을 반환받는다.

세션에 로그인 회원 정보를 보관한다. (loginService로부터 idpassword 발급받았다.)

이제 재요청한 주소로 이동한다.

  • 메인 화면에서 → mypage로 이동하려 했으나, 로그인이 되지 않아 로그인 페이지로 온 상태
  • 이럴 때 "redirect:"+redirectURL"을 입력시 로그인한 후, 이전에 요청한 주소로 이동한다.

 

📚 4. 나의 페이지

mypageWebConfig에서 예외된 주소가 아니기 때문에 매번 실행할 때마다 로그인 인증 체크를(LoginCheckInterceptor) 하게 된다.

로그인 된 상태에서 나의 페이지를 클릭했을 시에는 myPageLogin메서드가 실행된다.

스크린샷 2022-02-18 오후 4 11 39
  • 이제 반환 값으로 mypage/myPageList가 실행된다.

 

로그인 후 바로 나의 페이지가 실행되면 myPageLogin 메서드 실행되지 않고 mypage/myPageList 가 바로 실행된다.

mypage/myPageList가 실행된다.

<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="utf-8">
    <link th:href="@{/css/bootstrap.min.css}"
          href="../css/bootstrap.min.css" rel="stylesheet">
    <style>
        .container {
            max-width: 560px;
        }
    </style>
</head>
<body>

<div class="container">

    <div class="py-5 text-center">
        <h2>나의 페이지</h2>
    </div>

    <form action="item.html" th:action method="post">
        <div align="right">
            <input align="left" type="image" id="picture" name="picture">
            <input align="right" type="text" id="name" name="name" placeholder="이름">
        </div>
        &nbsp;
        <div align="right">
            <input  type="text" id="birthday" name="birthday" placeholder="생년월일">
        </div>
        &nbsp;
        <div align="right">
            <input type="text" id="school" name="school" placeholder="학교">
        </div>
        &nbsp;
        <div align="right">
            <input type="text" id="position" name="position" placeholder="포지션">
        </div>

        &nbsp;

        <div class="py-5 text-left">
            <h5>취미</h5>
        </div>

        <div align="left">
            <input type="text" id="book" name="book" placeholder="">
        </div>

        &nbsp;

        <div align="left">
            <input type="text" id="game" name="game" placeholder="게임">
        </div>

        &nbsp;

        <div align="left">
            <input type="text" id="study" name="study" placeholder="공부하고 있는 것">
        </div>

        &nbsp;

        <div align="left">
            <input type="text" id="blog" name="blog" placeholder="블로그">
        </div>
        <hr class="my-4">

        <div class="row">
            <div class="col">
                <button class="w-100 btn btn-primary btn-lg" type="submit">수정</button>
            </div>
            <div class="col">
                <button class="w-100 btn btn-secondary btn-lg"
                        onclick="location.href='items.html'"
                        th:onclick="|location.href='@{/}'|"
                        type="button">취소</button>
            </div>
        </div>

    </form>


    &nbsp;

    <div class="col">
        <form th:action="@{/mypage/logout}" method="post">
            <button class="w-100 btn btn-dark btn-lg" onclick="location.href='location.href='items.html'" type="submit">
                로그아웃
            </button>
        </form>

    </div>

</div> <!-- /container -->
</body>
</html>

 

스크린샷 2022-02-18 오후 5 44 01
  • mypage 화면 이다.

 

✔️ 이외 로그아웃 관련하여

form위치를 잘 확인하며 html에 로그아웃 소스를 삽입한다.

스크린샷 2022-02-18 오후 4 18 41
  • 로그아웃 버튼을 클릭시 /mypage/logout 소스가 post형식으로 실행된다.
스크린샷 2022-02-18 오후 4 21 55
  • 세션이 있을 경우 제거한다.
  • redirect로 되돌아가기를 한다.
  • 실행 시, 홈 화면으로 돌아가며 로그인을 다시해야 한다.

 

✔️ 나의 페이지 및 수정 edit

스크린샷 2022-02-21 오후 4 26 16
  • 저장된 데이터들이 화면에 띄워져야한다.

초기값을 설정해야한다.

스크린샷 2022-02-21 오후 4 28 11 스크린샷 2022-02-21 오후 5 14 54
  • 먼저 사용자 회원가입 정보를 저장할 때 초기값을 넣어주며, member.id를 저장한다.
    • 왜냐면, 나의 페이지 초기값들을 띄워져야하고, 어떤 user의 데이터인지 알아야한다.

 

로그인 Postmapping

스크린샷 2022-02-21 오후 4 39 15
  • 로그인에 성공할 시, session.setAttribute(SessionConst.LOGIN_MEMBER, loginmember); 세션에 Member를 저장한다.

 

나의 페이지 소스
스크린샷 2022-02-21 오후 4 49 53

  • 여기서 보면, 매개변수로 Member가 있는데 이거는 로그인할 때 저장한 Member이다. (출력해보면 아이디 패스워드에 해당하는 Member가 호출된다. 세션의 기능)
  • mypage/myPageList 뷰를 띄워준다.

 

mypage/myPageList

<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="utf-8">
    <link th:href="@{/css/bootstrap.min.css}"
          href="../css/bootstrap.min.css" rel="stylesheet">
    <style>
        .container {
            max-width: 560px;
        }
    </style>
</head>
<body>

<div class="container">

    <div class="py-5 text-center">
        <h2>나의 페이지</h2>
    </div>

    <form action="myPageEditForm.html" th:action th:object="${mypage}" method="post">
        <div align="right">
            <input align="left" type="image" id="picture" name="picture">
            <input align="right" type="text" id="name" th:field="*{name}" placeholder="이름" readonly>
        </div>
        &nbsp;
        <div align="right">
            <input  type="text" id="birthday" th:field="*{birthday}" placeholder="생년월일" readonly>
        </div>
        &nbsp;
        <div align="right">
            <input type="text" id="school" th:field="*{school}" placeholder="학교" readonly>
        </div>
        &nbsp;
        <div align="right">
            <input type="text" id="position" th:field="*{position}" placeholder="포지션" readonly>
        </div>

        &nbsp;

        <div class="py-5 text-left">
            <h5>취미</h5>
        </div>

        <div align="left">
            <input type="text" id="book" th:field="*{book}" placeholder="" readonly>
        </div>

        &nbsp;

        <div align="left">
            <input type="text" id="game" th:field="*{game}" placeholder="게임" readonly>
        </div>

        &nbsp;

        <div align="left">
            <input type="text" id="study" th:field="*{study}" placeholder="공부하고 있는 것" readonly>
        </div>
        &nbsp;

        <div align="left">
            <input type="text" id="blog" th:field="*{blog}" placeholder="블로그" readonly>
        </div>
        <hr class="my-4">

        <div class="row">
            <div class="col">
                <button class="w-100 btn btn-primary btn-lg"
                        th:onClick="|location.href='@{/mypage/{myPageId}/edit(myPageId=${mypage.id})}'|"
                        type="button">수정</button>
            </div>
            <div class="col">
                <button class="w-100 btn btn-secondary btn-lg"
                        onclick="location.href='items.html'"
                        th:onclick="|location.href='@{/}'|"
                        type="button">취소</button>
            </div>
        </div>

    </form>


    &nbsp;

    <div class="col">
        <form th:action="@{/mypage/logout}" method="post">
            <button class="w-100 btn btn-dark btn-lg" onclick="location.href='location.href='items.html'" type="submit">
                로그아웃
            </button>
        </form>

    </div>

</div> <!-- /container -->
</body>
</html>

여기서 th:action th:object="${mypage}"가 있는데 objectmypage로 하고, 자식? 다른 메소드들을 호출한다.
<input type="text" id="birthday" th:field="*{birthday}" placeholder="생년월일" readonly> 에서 *{birthday}와 같이

 

<button class="w-100 btn btn-primary btn-lg"
                        th:onClick="|location.href='@{/mypage/{myPageId}/edit(myPageId=${mypage.id})}'|"
                        type="button">수정</button>

'@{/mypage/{myPageId}/edit(myPageId=${mypage.id})}'이게 어떤 뜻인가?

  • locathost:8080/mypage/{여기는 mypage의 id를}/edit
  • 뒤에 (myPageId=${mypage.id})myPageIdmypageid를 넣어주겠다는 의미!

ex) mypage.id가 2라면
➡️ /mypage/2/edit이 된다!

 

✔️ 나의 페이지 수정 화면

스크린샷 2022-02-21 오후 5 00 17 스크린샷 2022-02-21 오후 5 01 06

@GetMapping("/{myPageId}/edit", @PathVariable("myPageId") Long id을 어떻게 선언한 것이며 받은 것인지 알아보면, 바로 윗줄에 html설명을 보면, {myPageId}/edit(myPageId=${mypage.id}) 와 같이 id를 전달하였다.

➡️ 전달한 것을 받은 것이다!

이후, myPageRepository에서 id를 통해 데이터를 찾고 mypage에 넣어 mypage/myPageEditForm에서 사용한다.

 

📚 5. 수정 화면

나의 페이지 → 나의 페이지 수정

스크린샷 2022-02-22 오후 4 23 31 스크린샷 2022-02-22 오후 4 29 26

나의 페이지 View 호출 (@GetMapping)

  • myPage 에서 id로 레포지토리에서 찾는다.
  • 다음, myPageEditForm html 화면을 띄운다.

 

✔️ 나의 페이지 수정 화면

스크린샷 2022-02-22 오후 4 23 42
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="utf-8">
    <link th:href="@{/css/bootstrap.min.css}"
          href="../css/bootstrap.min.css" rel="stylesheet">
    <style>
        .container {
            max-width: 560px;
        }
    </style>
</head>
<body>

<div class="container">

    <div class="py-5 text-center">
        <h2>나의 페이지 수정</h2>
    </div>

    <form th:action th:object="${mypage}" method="post">
        <h4>개인정보 수정</h4>

        <div>
            <label for="name" th:text="#{mypage.name}"></label>
            <input type="text" id="name" th:field="*{name}"
                   class="form-control" placeholder="이름 입력">
            <div class="field-error" th:errors="*{name}">
                이름 입력 오류
            </div>
        </div>
        <div>
            <label for="birthday" th:text="#{mypage.birthday}"></label>
            <input type="text" id="birthday" th:field="*{birthday}"
                   th:errorclass="field-error" class="form-control"
                   placeholder="생일 입력">
            <div class="field-error" th:errors="*{birthday}">
                생년월일 입력 오류
            </div>
        </div>
        <div>
            <label for="school" th:text="#{mypage.school}"></label>
            <input type="text" id="school" th:field="*{school}"
                   th:errorclass="field-error" class="form-control"
                   placeholder="학교 입력">
            <div class="field-error" th:errors="*{school}">
                학교 이름 오류
            </div>
        </div>
        <div>
            <label for="position" th:text="#{mypage.position}"></label>
            <input type="text" id="position" th:field="*{position}"
                   th:errorclass="field-error" class="form-control"
                   placeholder="포지션 입력">
            <div class="field-error" th:errors="*{position}">
                기술스택 입력 오류
            </div>
        </div>

        &nbsp;

        <h4>취미 수정</h4>

        <div>
            <label for="book" th:text="#{mypage.book}"></label>
            <input type="text" id="book" th:field="*{book}"
                   th:errorclass="field-error" class="form-control"
                   placeholder="책 입력">
            <div class="field-error" th:errors="*{book}">
                책 입력 오류
            </div>
        </div>
        <div>
            <label for="game" th:text="#{mypage.game}"></label>
            <input type="text" id="game" th:field="*{game}"
                   th:errorclass="field-error" class="form-control"
                   placeholder="게임 입력">
            <div class="field-error" th:errors="*{game}">
                게임 입력 오류
            </div>
        </div>
        <div>
            <label for="study" th:text="#{mypage.study}"></label>
            <input type="text" id="study" th:field="*{study}"
                   th:errorclass="field-error" class="form-control"
                   placeholder="스터디 입력">
            <div class="field-error" th:errors="*{study}">
                스터디 입력 오류
            </div>
        </div>
        <div>
            <label for="blog" th:text="#{mypage.blog}"></label>
            <input type="text" id="blog" th:field="*{blog}"
                   th:errorclass="field-error" class="form-control"
                   placeholder="포지션 입력">
            <div class="field-error" th:errors="*{blog}">
                개인 블로그 입력 오류
            </div>
        </div>

        <hr class="my-4">

        <div class="row">
            <div class="col">
                <button class="w-100 btn btn-primary btn-lg" type="submit">저장</button>
            </div>
            <div class="col">
                <button class="w-100 btn btn-secondary btn-lg"
                        onclick="location.href='myPageList.html'"
                        th:onclick="|location.href='@{/mypage}'|"
                        type="button">취소</button>
            </div>
        </div>

    </form>

</div> <!-- /container -->
</body>
</html>

여기서

<label for=""> : label 태그는 input 태그를 제어하여 상태를 변경하게 도와주는 태그
<form th:action th:object="${mypage}" method="post">
<input type="text" id="name" th:field="*{name}" : ${mypage} → th:field="*{name}" => mypage.name

이다.

 

스크린샷 2022-02-22 오후 4 26 01
  • 이제 데이터 수정을 한다.

저장 버튼을 클릭시, 아래 소스가 실행된다.

스크린샷 2022-02-22 오후 4 29 37
  • 검증 결과 오류가 발생시 다시 나의 페이지 수정화면으로 돌아간다.
  • 이제 나의 페이지 데이터를 업데이트한다.
  • 그리고, mypage로 재요청한다.
스크린샷 2022-02-22 오후 4 26 06
  • 나의 페이지 수정이 완료된 화면이다.
profile
"야, (오류 만났어?) 너두 (해결) 할 수 있어"

1개의 댓글

comment-user-thumbnail
2024년 9월 15일

I’ve always enjoyed strategic games, so when I tried my hand at online blackjack, I approached it like a chess match. I studied various strategies and tips, determined to apply them to my gameplay. One evening, I decided to test my skills with a larger bankroll. I carefully observed the dealer’s patterns and used basic strategy to guide https://magicalspincasino-france.com my decisions. My initial bets were conservative, but as I gained confidence and saw positive results, I gradually increased my stakes. By the end of the session, my strategic approach had paid off. I managed to turn a starting balance of $200 into over $1,000. It was incredibly satisfying to see that planning and strategy could lead to success, proving that blackjack is not just a game of luck but also one of skill.

답글 달기