I tried creating a web application with springboot [Internal management system]

## 1. Create a project

Project: Maven
Language: Java
SpringBoot: 3.2.1

Group: com.example
Artifact: SampleWeb
Name : SampleWeb
Description: Demo project for Spring Boot
Package name: com.example.demo
Dependencies

Spring Boot DevTools
Spring Web
Lombok
Thymeleaf
PostgreSQL Driver
Validation

## 2. Display login screen

(1) Understand the processing steps required to display the screen
① Access the page “http://~/login”　
② The controller in charge of “http://~/login” accepts (Java file)
③ Returns a file that defines screen display information (html file)
④ Display content of html file in browser


(2)　Create a controller
① Create a controller folder and LoginController file in src/main/java/com.example.demo
```src/main/java/com.example.demo.controller/LoginController.java
package com.example.demo.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;

import com.example.demo.form.LoginForm;
import com.example.demo.service.LoginService;

import lombok.RequiredArgsConstructor;

@Controller
@RequiredArgsConstructor
public class LoginController {

    private final LoginService service;

    @GetMapping("/login")
    public String view(Model model, LoginForm form) {
        return "login";
    }

    @PostMapping("/login")
    public String login(Model model, LoginForm form) {
        var userInfo = service.searchUserById(form.getLoginId());
        var isCorrectUserAuth = userInfo.isPresent()
                && form.getPassword().equals(userInfo.get().getPassword());
        if (isCorrectUserAuth) {
            return "redirect:/menu";
        } else {
            model.addAttribute("errorMsq", "The combination of login ID and password is incorrect");
            return "login";
        }
    }
}
```

② Create Login.html file in src/main/resources/templates

③　Create a form folder class to LoginForm file in src/main/java/com.example.demo
※ Form class is a class for passing data between html and Controller.

```src/main/java/com.example.demo/form/LoginForm.java
package com.example.demo.form;

import lombok.Data;

@Data
public class LoginForm {

    private String loginId;
    private String password;

    public String getLoginId() {
        return this.loginId;
    }
}
```
※ THYMELEAF is a template engine that can be described as HTML and can be displayed as it is. You can also create dynamic content by linking with Java code.
※ A template engine is a software for generating a web page by combining static content such as HTML and CSS with a dynamic content obtained from a database or API.

④ Add thymeleaf to login.html

```login.html
package com.example.demo.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;

import com.example.demo.form.LoginForm;

@Controller
public class LoginController {

    @GetMapping("/login")
    public String view(Model model, LoginForm form) {
        return "login";
    }

    @PostMapping("/login")
    public void login(LoginForm form) {
        System.out.println(form);
    }
}
```

(3) Implement processing for successful and failed logins!

① Create menu.html in src/main/resources/templates

```src/main/resources/templates/menu.html
<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="UTF-8" />
    <title>TOPmenu</title>
  </head>
  <body></body>
</html>
```

② Create a controller folder and MenuController file

```controller/MenuController.java
package com.example.demo.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class MenuController {

    @GetMapping(("/menu"))
    public String view() {
        return "menu";
    }

}

```
(4)Access the database using Spring Data JPA!

① Write the description to connect to the DB in application.properties

```resources/application.properties
spring.jpa.database=POSTGRESQL
spring.datasource.url=jdbc:postgresql://localhost:5432/SpringTestDB
spring.datasource.username=username
spring.datasource.password=password
```
② Create code to add to spring data jpa in pom.xml
※ pom.xml is a file that defines additional libraries required for implementation

```pom.xml
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
```
③ Create repository package and UserInfoRepository interface in src/main/java/com.example.demo

```src/main/java/com.example.demo/UserInfo.java
package com.example.demo.entity;

import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import lombok.Data;

@Entity
@Table(name = "user_info")
@Data
public class UserInfo {

    @Id
    @Column(name = "login_id")
    private String loginId;
    private String password;
}
```

④ Create service package and LoginService class in src/main/java/com.example.demo
```service/LoginService.java
package com.example.demo.service;

import java.util.Optional;

import org.springframework.stereotype.Service;

import com.example.demo.entity.UserInfo;
import com.example.demo.repository.UserInfoRepository;

import lombok.RequiredArgsConstructor;

@Service
@RequiredArgsConstructor
public class LoginService {

    private UserInfoRepository repository;

    public Optional<UserInfo> searchUserById(String loginId) {
        return repository.findById(loginId);
    }
}
```

⑤ Modify LoginController.java to call service class
```LoginController.java
package com.example.demo.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;

import com.example.demo.form.LoginForm;
import com.example.demo.service.LoginService;

import lombok.RequiredArgsConstructor;

@Controller
@RequiredArgsConstructor
public class LoginController {

    private final LoginService service;

    @GetMapping("/login")
    public String view(Model model, LoginForm form) {
        return "login";
    }

    @PostMapping("/login")
    public String login(Model model, LoginForm form) {
        var userInfo = service.searchUserById(form.getLoginId());
        var isCorrectUserAuth = userInfo.isPresent()
                && form.getPassword().equals(userInfo.get().getPassword());
        if (isCorrectUserAuth) {
            return "redirect:/menu";
        } else {
            model.addAttribute("errorMsq", "The combination of login ID and password is incorrect");
            return "login";
        }
    }
}
```
## What is Spring Data JPA?
- Libraries (functions) for using databases
- With basic syntax such as select and update using primary keys, you can execute SQL without writing a query.

#### Example of automatic SQL conversion by method name
```
findByAge(argument)
=> select * from table name where age = [Argument];

findByAge(argument 1, argument 2)
=> select * from table name where age = [Argument 1] and live = [Argument 2];

findByAgeIsNull()
=> select * from table name where age IS NULL;
```

## About Controller,Service,Repository
#### Controller
- Receive screen input
- Call the Service class
- Call the destination screen

#### Service
- Implement business logic such as check processing and DV access (calling Repository class)

#### Repository
- Perform DB access

(5)Implement hashed password comparison processing!
① Create config package and BeanDefine class in src/main/java/com.example.demo

```config/BeanDefine.java
package com.example.demo.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

@Configuration
public class BeanDefine {

    @Bean
    PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}
```
② Modify LoginController.java

```controller/LoginController.java
package com.example.demo.controller;

import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;

import com.example.demo.form.LoginForm;
import com.example.demo.service.LoginService;

import lombok.RequiredArgsConstructor;

//LoginScreenController
@Controller
@RequiredArgsConstructor
public class LoginController {

    // LoginService
    private final LoginService service;
    // passwordEncoder
    private final PasswordEncoder passwordEncoder;

    // Initial display
    // @Param modelモデル
    // @Param form入力情報
    // return 表示画面

    @GetMapping("/login")
    public String view(Model model, LoginForm form) {
        return "login";
    }

    // Login
    // @Param modelモデル
    // @Param form入力情報
    // return 表示画面

    @PostMapping("/login")
    public String login(Model model, LoginForm form) {
        var userInfo = service.searchUserById(form.getLoginId());
        var isCorrectUserAuth = userInfo.isPresent()
                && passwordEncoder.matches(form.getPassword(), userInfo.get().getPassword());
        if (isCorrectUserAuth) {
            return "redirect:/menu";
        } else {
            model.addAttribute("errorMsq", "The combination of login ID and password is incorrect");
            return "login";
        }
    }
}
```

(6) Manage error messages with property files

① Create messages.properties in the resources file
```resources/messages.properties
login.wrongInput=The combination of login ID and password is incorrect.
signup.existedLoginId=This is the login ID that you have already registered.
User registration completed.
```
② Create util package and AppUtil class in src/main/java/com.example.demo

```util/AppUtil.java
package com.example.demo.util;

import java.util.Locale;

import org.springframework.context.MessageSource;

//Application common class
public class AppUtil {

    public static String getMessage(MessageSource messageSource, String key, Object... params) {
        return messageSource.getMessage(key, params, Locale.ENGLISH);
    }

}
```

③ Modify LoginController.java
```LoginController.java
package com.example.demo.controller;

import org.springframework.context.MessageSource;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;

import com.example.demo.form.LoginForm;
import com.example.demo.service.LoginService;
import com.example.demo.util.AppUtil;

import lombok.RequiredArgsConstructor;

//LoginScreenController
@Controller
@RequiredArgsConstructor
public class LoginController {

    // LoginService
    private final LoginService service;
    // passwordEncoder
    private final PasswordEncoder passwordEncoder;

    private final MessageSource messageSource;

    // Initial display
    // @Param modelモデル
    // @Param form入力情報
    // return 表示画面

    @GetMapping("/login")
    public String view(Model model, LoginForm form) {
        return "login";
    }

    // Login
    // @Param modelモデル
    // @Param form入力情報
    // return 表示画面

    @PostMapping("/login")
    public String login(Model model, LoginForm form) {
        var userInfo = service.searchUserById(form.getLoginId());
        var isCorrectUserAuth = userInfo.isPresent()
                && passwordEncoder.matches(form.getPassword(), userInfo.get().getPassword());
        if (isCorrectUserAuth) {
            return "redirect:/menu";
        } else {
            var errorMsg = AppUtil.getMessage(messageSource, ErrorMessageConst.LOGIN_WRONG_INPUT);
            model.addAttribute("errorMsq", errorMsg);
            return "login";
        }
    }
}
```

④ Create constant package and ErrorMeddageConst class in src/main/java/com.example.demo
```constant/ErrorMeddageConst.java
package com.example.demo.constant;

//Error message ID constant class
public class ErrorMeddageConst {
    // Incorrect input on login screen
    public static final String LOGIN_WRONG_INPUT = "login.wrongInput";
}
```
(7)Implement the process to register in the DB user information table!

①　Create signup.html in template
```template/signup.html
<!DOCTYPE html>
<html xmlns:th="http://www.themeleaf.org" lang="en" xml:lang="en">//Add thymeleaf
  <head>
    <meta charset="utf-8" />
    <title>User registration screen</title>
  </head>
  <body>
    <2>Login screen</h2>
      <span th:text=${message}></span>
      <form th:action="@{/signup}" method="post" th:object="${signForm}">
        <div>
        <label>Login ID</label>
        <input type="text" th:field="*{signId}">
        </div>
        <div>
          <label>Password</label>
          <input type="password" th:field="*{password}">
        </div>
        <div>
          <input type="submit" value="login">
        </div>
      </form>
  </body>
</html>
```
② SignupController file in controller folder
```controller/SignupController.java
package com.example.demo.controller;

import org.springframework.context.MessageSource;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;

import com.example.demo.form.LoginForm;
import com.example.demo.service.LoginService;
import com.example.demo.service.SignupService;

import lombok.RequiredArgsConstructor;

//User registration screen Controller
@Controller
@RequiredArgsConstructor
public class SignupController {

    // User registration screen Service
    private final SignupService service;

    // Initial display
    // @Param modelモデル
    // @Param form入力情報
    // return 表示画面

    @GetMapping("/signup")
    public String view(Model model, SignupForm form) {
        return "signup";
    }

    // User registration screen
    // @Param modelモデル
    // @Param form入力情報
    // return 表示画面

    @PostMapping("/signup")
    public void signup(Model model, SignupForm form) {

    }
}
```
③ Create a Signup file in the form folder
```form/Signup.java
package com.example.demo.form;

import lombok.Data;

//User registration screen form
@Data
public class Signup {

    // LoginId
    private String loginId;
    // Password
    private String password;

}
```
④ Prepare to display in login.html
```template/login.html
<!DOCTYPE html>
<html xmlns:th="http://www.themeleaf.org" lang="en" xml:lang="en">//Add thymeleaf
  <head>
    <meta charset="utf-8" />
    <title>Login screen</title>
  </head>
  <body>
    <2>Login screen</h2>
      <span th:text=${errorMsg}></span>
      <form th:action="@{/login}" method="post" th:object="${loginForm}">
        <div>
        <label>Login ID</label>
        <input type="text" th:field="*{loginId}">
        </div>
        <div>
          <label>Password</label>
          <input type="password" th:field="*{password}">
        </div>
        <div>
          <input type="submit" value="login">
        </div>
      </form>
      <a th:href="@{/signup}"">user registration</a>
  </body>
</html>
```
⑤ Create a SignupService class in service

```service/SignupService.java

```import org.springframework.stereotype.Service;

import com.example.demo.entity.UserInfo;
import com.example.demo.form.SignupForm;
import com.example.demo.repository.UserInfoRepository;

import lombok.RequiredArgsConstructor;

//User registration screen

public class SignupService {

    private final UserInfoRepository repository;
    // Dozer Mapper
    private final Mapper mapper;
    // passwordEncoder
    private final PasswordEncoder passwordEncoder;

    public UserInfo resistUserById(SignupForm form) {
        var userInfo = mapper.map(form, UserInfo.class);

        var encodedPassword = passwordEncoder.encode(form.getPassword());
        userInfo.setPassword(encodedPassword);

        return repository.save(userInfo);
    }
}
```
⑥ Add Dozar Mapper to Project
```pom.xml
<dependency>
    <groupId>net.sf.dozar</groupId>
    <artifactId>dozar-mapper</artifactId>
    <version>5.6.0</version>
</dependency>
```
(8) Implement key duplication check before registering table data!

① Edit service/SignupService.java
```service/SignupService.java
package com.example.demo.service;

import dozer.DozerBeanMapper;

import java.util.Optional;

import org.apache.catalina.mapper.Mapper;
import org.springframework.context.MessageSource;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

import com.example.demo.entity.UserInfo;
import com.example.demo.form.SignupForm;
import com.example.demo.repository.UserInfoRepository;

import lombok.RequiredArgsConstructor;

//User registration screen
@Service
@RequiredArgsConstructor
public class SignupService {

    private final UserInfoRepository repository;
    // Dozer Mapper
    private final Mapper mapper;
    // passwordEncoder
    private final PasswordEncoder passwordEncoder;

    public Optinal<UserInfo> resistUserById(SignupForm form) {
        var userInfoExistedOpt = repository.findAll(form.getLoginId());
        if (userInfoExistedOpt.isPresent()) {
            return Optional.empty();
        }

        var userInfo = mapper.map(form, UserInfo.class);
        var encodedPassword = passwordEncoder.encode(form.getPassword());
        userInfo.setPassword(encodedPassword);

        return Optional.of(repository.save(userInfo));
    }
}
```

② Edit controller/SignupController.java
```controller/SignupController.java
package com.example.demo.controller;

import org.springframework.context.MessageSource;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;

import com.example.demo.constant.MessageConst;
import com.example.demo.form.LoginForm;
import com.example.demo.service.LoginService;
import com.example.demo.service.SignupService;
import com.example.demo.util.AppUtil;

import lombok.RequiredArgsConstructor;

//User registration screen Controller
@Controller
@RequiredArgsConstructor
public class SignupController {

    // User registration screen Service
    private final SignupService service;

    // MessageSource
    private final MessageSource messageSource;

    // Initial displa
    // @Param modelモデル
    // @Param form入力情報
    // return 表示画面

    @GetMapping("/signup")
    public String view(Model model, SignupForm form) {
        return "signup";
    }

    // User registration screen
    // @Param modelモデル
    // @Param form入力情報
    // return 表示画面

    @PostMapping("/signup")
    public void signup(Model model, SignupForm form) {
        var userInfoOpt = service.resistUserInfo(form);
        var message = AppUtil.getMessage(messageSource, judgeMessagekey(userInfoOpt));
        model.addAttribute("message", message);

    }

    private String judgeMessagekey(Optinal<UserInfo> userInfoOpt) {
        if (userInfoOpt.isEmpty()) {
            return MessageConst.SIGNUP_EXISTED_LOGIN_ID;
        } else {
            return MessageConst.SIGNUP_RESIST_SUCCEED;
        }
    }
}
```
③　Edit constant/ErrorMessageConst.java
```constant/ErrorMessageConst.java
package com.example.demo.constant;

//Error message ID constant class
public class MessageConst {
    // Incorrect input on login screen
    public static final String LOGIN_WRONG_INPUT = "login.wrongInput";
    // User registration screen: Already registered login ID
    public static final String SIGNUP_EXISTED_LOGIN_ID = "signup.extendLoginId";
    // User registration screen:User registration completed
    public static final String SIGNUP_RESIST_SUCCEED = "signup.resistSucceed";
}
```
④ constant/ErrorMessageConst.java ⇨ Change to constant/MessageConst.java

⑤ Edit controller/SignupController.java

```controller/SignupController.java
package com.example.demo.controller;

import org.springframework.context.MessageSource;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;

import com.example.demo.constant.MessageConst;
import com.example.demo.form.LoginForm;
import com.example.demo.service.LoginService;
import com.example.demo.service.SignupService;
import com.example.demo.util.AppUtil;

import lombok.RequiredArgsConstructor;

//User registration screen Controller
@Controller
@RequiredArgsConstructor
public class SignupController {

    // User registration screen Service
    private final SignupService service;

    // MessageSource
    private final MessageSource messageSource;

    // Initial displa
    // @Param modelモデル
    // @Param form入力情報
    // return 表示画面

    @GetMapping("/signup")
    public String view(Model model, SignupForm form) {
        return "signup";
    }

    // User registration screen
    // @Param modelモデル
    // @Param form入力情報
    // return 表示画面

    @PostMapping("/signup")
    public void signup(Model model, SignupForm form) {
        var userInfoOpt = service.resistUserInfo(form);
        var message = AppUtil.getMessage(messageSource, judgeMessagekey(userInfoOpt));
        model.addAttribute("message", message);

    }

    private String judgeMessagekey(Optinal<UserInfo> userInfoOpt) {
        if (userInfoOpt.isEmpty()) {
            return MessageConst.SIGNUP_EXISTED_LOGIN_ID;
        } else {
            return MessageConst.SIGNUP_RESIST_SUCCEED;
        }
    }
}
```
(9) Arrange the screen layout using Bootstrap
[Bootstrap - org.webjars](https://mvnrepository.com/artifact/org.webjars/bootstrap)
① Introducing Bootstrap to pom.xml
```pom.xml
<!-- https://mvnrepository.com/artifact/org.webjars/jquery -->
       <dependency>
      <groupId>org.webjars</groupId>
       <artifactId>jquery</artifactId>
       <version>3.7.1</version>
　　　　</dependency>

		<!-- https://mvnrepository.com/artifact/org.webjars/bootstrap -->
        <dependency>
         <groupId>org.webjars</groupId>
         <artifactId>bootstrap</artifactId>
        <version>5.3.2</version>
        </dependency>
```
② Add Boostrap code to login.html

```login.html


```



## Reference site
[【Spring Bootで作るWebアプリ | part3】プログラムの実行環境を作成しよう！【初学者向け】](https://www.youtube.com/watch?v=QTBsCxqGAPc&t=89s)
[【Spring Bootで作るWebアプリ | part4】画面を表示してみよう！【初学者向け】](https://www.youtube.com/watch?v=8Kjdn_pqdO8&t=624s)
[【Spring Bootで作るWebアプリ | part4 補足】GetMappingの別実装方法について【初学者向け】](https://www.youtube.com/watch?v=xA_LTUI6X1o)
[【Spring Bootで作るWebアプリ | part5】ログイン画面の入力値を受け取る！【初学者向け】](https://www.youtube.com/watch?v=Usf1sh0pTWQ)
[【Spring Bootで作るWebアプリ | part6】ログイン成功時、失敗時の処理を実装する！【初学者向け】](https://www.youtube.com/watch?v=RbrCJYm82lQ)
[【Spring Bootで作るWebアプリ | part7】開発用PCにデータベースを構築する！【初学者向け】](https://www.youtube.com/watch?v=ai5HILL2P6Q)
[【Spring Bootで作るWebアプリ | part9】ソースコードにコメントを書いてみよう！【初学者向け】](https://www.youtube.com/watch?v=FBNjjnrjHsc)
[【Spring Bootで作るWebアプリ | part10】ハッシュ化したパスワードの比較処理を実装する！【初学者向け】](https://www.youtube.com/watch?v=Qt3bjKItFac)
[【Spring Bootで作るWebアプリ | part11】エラーメッセージをプロパティファイルで管理する【初学者向け】](https://www.youtube.com/watch?v=eoRxkHsSn2w)
[【Spring Bootで作るWebアプリ | part12】DBのユーザー情報テーブルへ登録を行う処理を実装する！【初学者向け】](https://www.youtube.com/watch?v=8v8Ybbcde_k)
[【Spring Bootで作るWebアプリ | part13】テーブルデータ登録前のキー重複チェックを実装する！【初学者向け】](https://www.youtube.com/watch?v=PkdLDPSdeiE)
[【Spring Bootで作るWebアプリ | part14】Bootstrapを使って画面レイアウトを整えよう！【初学者向け】](https://www.youtube.com/watch?v=xCgxL0Y6ZBE)