diff --git a/BE/.DS_Store b/BE/.DS_Store new file mode 100644 index 0000000..40136b4 Binary files /dev/null and b/BE/.DS_Store differ diff --git a/BE/.gitignore b/BE/.gitignore new file mode 100644 index 0000000..c2065bc --- /dev/null +++ b/BE/.gitignore @@ -0,0 +1,37 @@ +HELP.md +.gradle +build/ +!gradle/wrapper/gradle-wrapper.jar +!**/src/main/**/build/ +!**/src/test/**/build/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache +bin/ +!**/src/main/**/bin/ +!**/src/test/**/bin/ + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr +out/ +!**/src/main/**/out/ +!**/src/test/**/out/ + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ + +### VS Code ### +.vscode/ diff --git a/BE/build.gradle b/BE/build.gradle new file mode 100644 index 0000000..d1e0432 --- /dev/null +++ b/BE/build.gradle @@ -0,0 +1,38 @@ +plugins { + id 'java' + id 'org.springframework.boot' version '2.7.7' + id 'io.spring.dependency-management' version '1.0.15.RELEASE' +} + +group = 'Remoa' +version = '0.0.1-SNAPSHOT' +sourceCompatibility = '1.8' + +configurations { + compileOnly { + extendsFrom annotationProcessor + } +} + +repositories { + mavenCentral() +} + +dependencies { + implementation 'org.springframework.boot:spring-boot-starter-data-jpa' + implementation 'org.springframework.boot:spring-boot-starter-security' + implementation 'org.springframework.boot:spring-boot-starter-validation' + implementation 'org.springframework.boot:spring-boot-starter-web' + compileOnly 'org.projectlombok:lombok' + runtimeOnly 'com.mysql:mysql-connector-j' + annotationProcessor 'org.projectlombok:lombok' + testImplementation 'org.springframework.boot:spring-boot-starter-test' + testImplementation 'org.springframework.security:spring-security-test' + + //json simple + implementation group: 'com.googlecode.json-simple', name: 'json-simple', version: '1.1.1' +} + +tasks.named('test') { + useJUnitPlatform() +} diff --git a/BE/build/libs/.DS_Store b/BE/build/libs/.DS_Store new file mode 100644 index 0000000..e290b0c Binary files /dev/null and b/BE/build/libs/.DS_Store differ diff --git a/BE/build/libs/BE-ver_230125_2.jar b/BE/build/libs/BE-ver_230125_2.jar new file mode 100644 index 0000000..52107ae Binary files /dev/null and b/BE/build/libs/BE-ver_230125_2.jar differ diff --git a/BE/build/libs/BE-ver_230203_3.jar b/BE/build/libs/BE-ver_230203_3.jar new file mode 100644 index 0000000..3cab401 Binary files /dev/null and b/BE/build/libs/BE-ver_230203_3.jar differ diff --git a/BE/gradle/wrapper/gradle-wrapper.jar b/BE/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..249e583 Binary files /dev/null and b/BE/gradle/wrapper/gradle-wrapper.jar differ diff --git a/BE/gradle/wrapper/gradle-wrapper.properties b/BE/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..070cb70 --- /dev/null +++ b/BE/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/BE/gradlew b/BE/gradlew new file mode 100644 index 0000000..a69d9cb --- /dev/null +++ b/BE/gradlew @@ -0,0 +1,240 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit + +APP_NAME="Gradle" +APP_BASE_NAME=${0##*/} + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/BE/gradlew.bat b/BE/gradlew.bat new file mode 100644 index 0000000..53a6b23 --- /dev/null +++ b/BE/gradlew.bat @@ -0,0 +1,91 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/BE/settings.gradle b/BE/settings.gradle new file mode 100644 index 0000000..2fe2c81 --- /dev/null +++ b/BE/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'BE' diff --git a/BE/src/.DS_Store b/BE/src/.DS_Store new file mode 100644 index 0000000..1d0de0f Binary files /dev/null and b/BE/src/.DS_Store differ diff --git a/BE/src/main/.DS_Store b/BE/src/main/.DS_Store new file mode 100644 index 0000000..4e604ed Binary files /dev/null and b/BE/src/main/.DS_Store differ diff --git a/BE/src/main/java/Remoa/BE/BeApplication.java b/BE/src/main/java/Remoa/BE/BeApplication.java new file mode 100644 index 0000000..d5d8f38 --- /dev/null +++ b/BE/src/main/java/Remoa/BE/BeApplication.java @@ -0,0 +1,35 @@ +package Remoa.BE; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +import javax.annotation.PostConstruct; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; + +@SpringBootApplication +public class BeApplication { + + @Value("${uploadFolder}") + private String uploadFolder; + + public static void main(String[] args) { + SpringApplication.run(BeApplication.class, args); + } + + @PostConstruct + public void createUploadFolder() { + Path upload = Paths.get(uploadFolder); + try { + if (!Files.exists(upload)) { + Files.createDirectory(upload); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + +} diff --git a/BE/src/main/java/Remoa/BE/config/DbInit.java b/BE/src/main/java/Remoa/BE/config/DbInit.java new file mode 100644 index 0000000..d61e705 --- /dev/null +++ b/BE/src/main/java/Remoa/BE/config/DbInit.java @@ -0,0 +1,65 @@ +package Remoa.BE.config; + +import Remoa.BE.domain.Category; +import Remoa.BE.domain.Member; +import Remoa.BE.domain.MemberCategory; +import Remoa.BE.repository.MemberRepository; +import Remoa.BE.service.CategoryService; +import Remoa.BE.service.SignupService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.security.core.parameters.P; +import org.springframework.stereotype.Component; + +import javax.annotation.PostConstruct; +import java.util.ArrayList; +import java.util.List; + +@Slf4j +@Component +@RequiredArgsConstructor +public class DbInit { + private final SignupService signupService; + private final CategoryService categoryService; + + @PostConstruct + public void createAdminUser() { + if (signupService.isAdminExist()) { + //do nothing + log.info("============Admin is already exist============"); + } else { + Member adminMember = new Member(); + adminMember.setEmail("spparta@gmail.com"); + adminMember.setPassword("admin"); + adminMember.setName("admin"); + adminMember.setBirth("00000000"); + adminMember.setSex(true); + adminMember.setPhoneNumber("01000000000"); + adminMember.setOneLineIntroduction("관리자입니다."); + adminMember.setTermConsent(true); + adminMember.setRole("ROLE_ADMIN"); + this.signupService.join(adminMember); + } + } + + @PostConstruct + public void initCategories() { + if (!categoryService.findAllCategories().isEmpty()) { + //do nothing + log.info("==========Categories is already set=========="); + } else { + Category idea = new Category("idea"); + Category marketing = new Category("marketing"); + Category design = new Category("design"); + Category video = new Category("video"); + Category etc = new Category("etc"); + this.categoryService.persistCategory(new Category[]{idea, marketing, design, video, etc}); + } + } + + @PostConstruct + public void addCategories() { + /* 카페고리를 더 추가하고 싶을 때 사용할 예정. */ + } + +} diff --git a/BE/src/main/java/Remoa/BE/config/SecureConfig.java b/BE/src/main/java/Remoa/BE/config/SecureConfig.java new file mode 100644 index 0000000..7482baa --- /dev/null +++ b/BE/src/main/java/Remoa/BE/config/SecureConfig.java @@ -0,0 +1,47 @@ +package Remoa.BE.config; + +import lombok.NoArgsConstructor; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.security.web.SecurityFilterChain; + +@EnableWebSecurity +@Configuration +public class SecureConfig { + + @Bean + public PasswordEncoder passwordEncoder() { + return new BCryptPasswordEncoder(); + } + + @Bean + public SecurityFilterChain configure(HttpSecurity http) throws Exception { + http + .authorizeRequests() + //api 명세 확정 후 재확인 핋요 + .antMatchers("/**").permitAll() + /*.antMatchers("/").permitAll() + .antMatchers("/login").permitAll() + .antMatchers("/logout").permitAll() + .antMatchers("/upload").permitAll() + .antMatchers("/download").permitAll() + .antMatchers("/signup/**").permitAll() + .antMatchers("/mypage").authenticated()*/ + .antMatchers("/admin").hasRole("ADMIN") + .antMatchers("/user").hasAnyRole("ADMIN", "USER") + .anyRequest().authenticated() + .and() + + .sessionManagement() + .maximumSessions(-1) //세션 제한 없음 + .maxSessionsPreventsLogin(false); //중복 접속시 마지막 세션만 유지 + + http.csrf().disable(); + + return http.build(); + } +} diff --git a/BE/src/main/java/Remoa/BE/controller/FileController.java b/BE/src/main/java/Remoa/BE/controller/FileController.java new file mode 100644 index 0000000..1b44902 --- /dev/null +++ b/BE/src/main/java/Remoa/BE/controller/FileController.java @@ -0,0 +1,51 @@ +package Remoa.BE.controller; + +import Remoa.BE.service.FileService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.CrossOrigin; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.multipart.MultipartFile; + +import java.util.*; + +@Slf4j +@RestController +@RequiredArgsConstructor +@CrossOrigin(origins="*") +public class FileController { + + private final FileService fileService; + + @PostMapping("/upload") + public ResponseEntity upload(@RequestParam("files") MultipartFile[] files // 파일 받기 + ) { + // 1. response 객체 생성 + HashMap resultMap = new HashMap<>(); + HashMap responseData = new HashMap<>(); + + // 2. 받은 파일 데이터 확인 + List> fileNames = new ArrayList<>(); + Arrays.stream(files).forEach(f -> { + HashMap map = new HashMap<>(); + String originalFilename = f.getOriginalFilename(); + String extension = originalFilename.substring(originalFilename.lastIndexOf(".")); + String saveFileName = UUID.randomUUID() + extension; + log.info("originalFilename = {}", originalFilename); + log.info("saveFileName = {}", saveFileName); + map.put("fileName", saveFileName); + map.put("fileSize", f.getSize()); + + fileNames.add(map); + fileService.save(f, saveFileName); + }); + + // 3. response 하기 + responseData.put("files", fileNames); + resultMap.put("response", responseData); + return ResponseEntity.ok().body(resultMap); + } +} diff --git a/BE/src/main/java/Remoa/BE/controller/HomeController.java b/BE/src/main/java/Remoa/BE/controller/HomeController.java new file mode 100644 index 0000000..e854f3b --- /dev/null +++ b/BE/src/main/java/Remoa/BE/controller/HomeController.java @@ -0,0 +1,30 @@ +package Remoa.BE.controller; + +import Remoa.BE.domain.Member; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.CrossOrigin; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpSession; + +@Slf4j +@RestController +@CrossOrigin(origins = "*") +public class HomeController { + + @GetMapping("/") + public String home(HttpServletRequest request) { + HttpSession session = request.getSession(false); + + if (session == null) { + return "hello, stranger"; + } + + Member loginMember = (Member) session.getAttribute("loginMember"); + + return "hello, " + loginMember.getName(); + } + +} \ No newline at end of file diff --git a/BE/src/main/java/Remoa/BE/controller/KakaoController.java b/BE/src/main/java/Remoa/BE/controller/KakaoController.java new file mode 100644 index 0000000..227e099 --- /dev/null +++ b/BE/src/main/java/Remoa/BE/controller/KakaoController.java @@ -0,0 +1,93 @@ +package Remoa.BE.controller; + +import Remoa.BE.domain.Member; +import Remoa.BE.form.KakaoSignupForm; +import Remoa.BE.service.KakaoService; +import Remoa.BE.service.SignupService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.context.SecurityContext; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.web.context.HttpSessionSecurityContextRepository; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.*; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +@RestController +@Slf4j +@RequiredArgsConstructor +@CrossOrigin(origins="*") +public class KakaoController { + + private final KakaoService ks; + private final SignupService signupService; + + @GetMapping("/login/kakao") + public void getCI(@RequestParam String code, HttpServletResponse response, HttpServletRequest request) throws IOException { + log.info("code = " + code); + + // 액세스 토큰과 유저정보 받기 + String access_token = ks.getToken(code); + Map userInfo = ks.getUserInfo(access_token); + + log.info("userInfo = {}", userInfo.values()); + + Long kakaoId = Long.parseLong((String) userInfo.get("id")); + Member kakaoMember = ks.distinguishKakaoId(kakaoId); + + log.info("kakaoId = {}", kakaoId); + + if (kakaoMember == null) { + //kakaoId가 db에 없으므로 kakaoMember가 null이므로 회원가입하지 않은 회원. 따라서 회원가입이 필요하므로 회원가입하는 uri로 redirect 시켜주어야 함. + response.sendRedirect("/signup/kakao"); //현재 api명세서가 미완성이라 임의로 카카오 회원가입 페이지를 정해서 써두었습니다. + //현재는 편의상 위처럼 redirect를 써두었으나 이후 작업에서는 프론트에 카카오에서 받아온 사용자 정보를 넘겨야 하므로 return값으로 userInfo에서 닉네임와 이메일 등을 받아와야 합니다. + } else { + //if문에 걸리지 않았다면 이미 회원가입이 진행돼 db에 kakaoId가 있는 유저이므로 kakaoMember가 존재하므로 LoginController처럼 로그인 처리 하면 됩니다. + securityLoginWithoutLoginForm(request, kakaoMember); + } + + } + + @PostMapping("/signup/kakao") + public void signupKakaoMember(KakaoSignupForm form) { + + Member member = new Member(); + member.setEmail(form.getEmail()); + member.setKakaoId(form.getKakaoId()); + member.setName(form.getName()); + member.setBirth(form.getBirth()); + member.setSex(form.getSex()); + member.setPhoneNumber(form.getPhoneNumber()); + member.setTermConsent(form.getTermConsent()); + + signupService.join(member); + } + + private void securityLoginWithoutLoginForm(HttpServletRequest request, Member member) { + + //로그인 세션에 들어갈 권한을 설정합니다. + List list = new ArrayList<>(); + list.add(new SimpleGrantedAuthority("ROLE_USER")); + + SecurityContext sc = SecurityContextHolder.getContext(); + //아이디, 패스워드, 권한을 설정합니다. 아이디는 Object단위로 넣어도 무방하며 + //패스워드는 null로 하여도 값이 생성됩니다. + sc.setAuthentication(new UsernamePasswordAuthenticationToken(member, null, list)); + HttpSession session = request.getSession(true); + session.setAttribute("loginMember", member); + + //위에서 설정한 값을 Spring security에서 사용할 수 있도록 세션에 설정해줍니다. + session.setAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY, sc); + } +} \ No newline at end of file diff --git a/BE/src/main/java/Remoa/BE/controller/LoginController.java b/BE/src/main/java/Remoa/BE/controller/LoginController.java new file mode 100644 index 0000000..75a2d61 --- /dev/null +++ b/BE/src/main/java/Remoa/BE/controller/LoginController.java @@ -0,0 +1,69 @@ +package Remoa.BE.controller; + +import Remoa.BE.domain.Member; +import Remoa.BE.form.LoginForm; +import Remoa.BE.service.LoginService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.context.SecurityContext; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.web.context.HttpSessionSecurityContextRepository; +import org.springframework.web.bind.annotation.*; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpSession; +import javax.validation.Valid; +import java.util.ArrayList; +import java.util.List; + +@Slf4j +@RestController +@RequiredArgsConstructor +@CrossOrigin(origins="*") +public class LoginController { + + private final LoginService loginService; + + @GetMapping("/login") + public String loginForm() { + return "로그인 페이지 입니다."; + } + + @PostMapping("/login") + public String login(@Valid @RequestBody LoginForm form, HttpServletRequest request) { + + log.info("login process activate"); + + Member loginMember = loginService.login(form.getEmail(), form.getPassword()); + + if (loginMember == null) { + log.info("login process fail"); + return "login fail"; + } + log.info("login process success"); + + securityLoginWithoutLoginForm(request, loginMember); + + return loginMember.getNickname(); + } + + private void securityLoginWithoutLoginForm(HttpServletRequest request, Member member) { + + //로그인 세션에 들어갈 권한을 설정합니다. + List list = new ArrayList<>(); + list.add(new SimpleGrantedAuthority("ROLE_USER")); + + SecurityContext sc = SecurityContextHolder.getContext(); + //아이디, 패스워드, 권한을 설정합니다. 아이디는 Object단위로 넣어도 무방하며 + //패스워드는 null로 하여도 값이 생성됩니다. + sc.setAuthentication(new UsernamePasswordAuthenticationToken(member, null, list)); + HttpSession session = request.getSession(true); + session.setAttribute("loginMember", member); + + //위에서 설정한 값을 Spring security에서 사용할 수 있도록 세션에 설정해줍니다. + session.setAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY, sc); + } +} \ No newline at end of file diff --git a/BE/src/main/java/Remoa/BE/controller/SignupController.java b/BE/src/main/java/Remoa/BE/controller/SignupController.java new file mode 100644 index 0000000..452f984 --- /dev/null +++ b/BE/src/main/java/Remoa/BE/controller/SignupController.java @@ -0,0 +1,46 @@ +package Remoa.BE.controller; + +import Remoa.BE.form.SignupForm; +import Remoa.BE.domain.Member; +import Remoa.BE.service.SignupService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.*; + +import javax.validation.Valid; + +@Slf4j +@RestController +@RequiredArgsConstructor +@CrossOrigin(origins="*") +public class SignupController { + + private final SignupService memberService; + + @GetMapping("/signup") + public String createForm() { + return "회원가입 페이지 입니다."; + } + + @PostMapping("/signup") + public String create(@RequestBody @Valid SignupForm form) { + + Member member = new Member(); + member.setEmail(form.getEmail()); + member.setPassword(form.getPassword()); + member.setName(form.getName()); + member.setBirth(form.getBirth()); + member.setSex(form.getSex()); + member.setPhoneNumber(form.getPhoneNumber()); + member.setTermConsent(form.getTermConsent()); + + try{ + memberService.join(member); + } + catch (IllegalStateException e){ + return e.getMessage(); // 에러 메세지 프론트에 반환 + } + + return "signup success"; + } +} \ No newline at end of file diff --git a/BE/src/main/java/Remoa/BE/domain/Category.java b/BE/src/main/java/Remoa/BE/domain/Category.java new file mode 100644 index 0000000..155a5b8 --- /dev/null +++ b/BE/src/main/java/Remoa/BE/domain/Category.java @@ -0,0 +1,28 @@ +package Remoa.BE.domain; + +import lombok.Getter; +import lombok.NoArgsConstructor; + +import javax.persistence.*; + +@Entity +@Inheritance(strategy = InheritanceType.SINGLE_TABLE) +@NoArgsConstructor +@Getter +public class Category { + + public Category(String name) { + this.name = name; + } + + @Id + @GeneratedValue + @Column(name = "category_id") + private Long categoryId; + + @OneToOne(mappedBy = "category", fetch = FetchType.LAZY) + private Post post; + + private String name; + +} diff --git a/BE/src/main/java/Remoa/BE/domain/Comment.java b/BE/src/main/java/Remoa/BE/domain/Comment.java new file mode 100644 index 0000000..1688ac8 --- /dev/null +++ b/BE/src/main/java/Remoa/BE/domain/Comment.java @@ -0,0 +1,26 @@ +package Remoa.BE.domain; + +import lombok.Getter; +import lombok.Setter; + +import javax.persistence.*; + +@Entity +@Getter +@Setter +public class Comment { + + @Id + @GeneratedValue + @Column(name = "comment_id") + private Long commentId; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "post_id") + private Post post; + + private String comment; + + @Column(name = "commented_time") + private String commentedTime; +} \ No newline at end of file diff --git a/BE/src/main/java/Remoa/BE/domain/CommentBookmark.java b/BE/src/main/java/Remoa/BE/domain/CommentBookmark.java new file mode 100644 index 0000000..b5bbd68 --- /dev/null +++ b/BE/src/main/java/Remoa/BE/domain/CommentBookmark.java @@ -0,0 +1,36 @@ +package Remoa.BE.domain; + +import lombok.AccessLevel; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import javax.persistence.*; + +@Getter +@Setter +@Entity +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class CommentBookmark { + + @Id + @GeneratedValue + @Column(name = "comment_bookmark_id") + private Long commentBookmarkId; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "member_id") + private Member member; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "comment_id") + private Comment comment; + + public static CommentBookmark createCommentBookmark(Member member, Comment comment) { + CommentBookmark commentBookmark = new CommentBookmark(); + commentBookmark.setComment(comment); + commentBookmark.setMember(member); + + return commentBookmark; + } +} diff --git a/BE/src/main/java/Remoa/BE/domain/CommentLike.java b/BE/src/main/java/Remoa/BE/domain/CommentLike.java new file mode 100644 index 0000000..bbd8545 --- /dev/null +++ b/BE/src/main/java/Remoa/BE/domain/CommentLike.java @@ -0,0 +1,36 @@ +package Remoa.BE.domain; + +import lombok.AccessLevel; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import javax.persistence.*; + +@Getter +@Setter +@Entity +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class CommentLike { + + @Id + @GeneratedValue + @Column(name = "comment_like_id") + private Long commentLikeId; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "member_id") + private Member member; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "comment_id") + private Comment comment; + + public static CommentLike createCommentLike(Member member, Comment comment) { + CommentLike commentLike = new CommentLike(); + commentLike.setComment(comment); + commentLike.setMember(member); + + return commentLike; + } +} \ No newline at end of file diff --git a/BE/src/main/java/Remoa/BE/domain/Follow.java b/BE/src/main/java/Remoa/BE/domain/Follow.java new file mode 100644 index 0000000..24ac5fb --- /dev/null +++ b/BE/src/main/java/Remoa/BE/domain/Follow.java @@ -0,0 +1,38 @@ +package Remoa.BE.domain; + +import lombok.Getter; +import lombok.Setter; + +import javax.persistence.*; + +@Entity +@Getter +@Setter +public class Follow { + + @Id + @GeneratedValue + @Column(name = "follow_id") + private Long followId; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "from_member_id") + private Member fromMember; + + @Column(name = "to_member_id") + private Long toMemberId; + + public void setMember(Member member) { + this.fromMember = member; + member.getFollows().add(this); + } + + public static Follow followSomeone(Member toMember, Member fromMember) { + Follow follow = new Follow(); + follow.setFromMember(fromMember); + follow.setToMemberId(toMember.getMemberId()); + + return follow; + } + +} diff --git a/BE/src/main/java/Remoa/BE/domain/Member.java b/BE/src/main/java/Remoa/BE/domain/Member.java new file mode 100644 index 0000000..e4040d1 --- /dev/null +++ b/BE/src/main/java/Remoa/BE/domain/Member.java @@ -0,0 +1,120 @@ +package Remoa.BE.domain; + +import lombok.Getter; +import lombok.Setter; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.crypto.password.PasswordEncoder; + +import javax.persistence.*; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +@Getter +@Setter +@Entity +public class Member implements UserDetails { + + @Id + @GeneratedValue + @Column(name = "member_id") + private Long memberId; + + @Column(name = "kakao_id") + private Long kakaoId; + + private String email; + + private String password; + + private String name; + + //23.1.19 추가 + private String nickname; + + private String birth; + + private Boolean sex; + + @Column(name = "phone_number") + private String phoneNumber; + + private String university; + + @Column(name = "one_line_introduction") + private String oneLineIntroduction; + + @Column(name = "term_consent") + private Boolean termConsent; + + @OneToMany(mappedBy = "member") + private List posts = new ArrayList<>(); + + @OneToMany(mappedBy = "member", cascade = {CascadeType.ALL}) + private List memberCategories = new ArrayList(); + + @OneToMany(mappedBy = "member", cascade = {CascadeType.ALL}) + private List commentBookmarks = new ArrayList(); + + @OneToMany(mappedBy = "member", cascade = {CascadeType.ALL}) + private List commentLikes = new ArrayList(); + + @OneToMany(mappedBy = "fromMember") + private List follows = new ArrayList(); + + private String role; + + public Member hashPassword(PasswordEncoder passwordEncoder) { + this.password = passwordEncoder.encode(this.password); + return this; + } + + public Boolean checkPassword(String plainPassword, PasswordEncoder passwordEncoder) { + return passwordEncoder.matches(plainPassword, this.password); + } + + public void addMemberCategory(MemberCategory memberCategory) { + memberCategories.add(memberCategory); + } + + public void addCommentBookmark(MemberCategory memberCategory) { + memberCategories.add(memberCategory); + } + + public void addCommentLike(MemberCategory memberCategory) { + memberCategories.add(memberCategory); + } + + @Override + public Collection getAuthorities() { + Collection authorities = new ArrayList<>(); + + for(String role : role.split(",")){ + authorities.add(new SimpleGrantedAuthority(role)); + } + return authorities; + } + + public String getUsername() { + return null; + } + + public boolean isAccountNonExpired() { + return false; + } + + public boolean isAccountNonLocked() { + return false; + } + + public boolean isCredentialsNonExpired() { + return false; + } + + public boolean isEnabled() { + return false; + } + +} diff --git a/BE/src/main/java/Remoa/BE/domain/MemberCategory.java b/BE/src/main/java/Remoa/BE/domain/MemberCategory.java new file mode 100644 index 0000000..b6d2495 --- /dev/null +++ b/BE/src/main/java/Remoa/BE/domain/MemberCategory.java @@ -0,0 +1,37 @@ +package Remoa.BE.domain; + +import lombok.AccessLevel; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import javax.persistence.*; + +@Getter +@Setter +@Entity +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class MemberCategory { + + @Id + @GeneratedValue + @Column(name = "member_category_id") + private Long memberCategoryId; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "member_id") + private Member member; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "category_id") + private Category category; + + public static MemberCategory createMemberCategory(Member member, Category category) { + MemberCategory memberCategory = new MemberCategory(); + memberCategory.setMember(member); + memberCategory.setCategory(category); + + return memberCategory; + } + +} diff --git a/BE/src/main/java/Remoa/BE/domain/Post.java b/BE/src/main/java/Remoa/BE/domain/Post.java new file mode 100644 index 0000000..053e34b --- /dev/null +++ b/BE/src/main/java/Remoa/BE/domain/Post.java @@ -0,0 +1,57 @@ +package Remoa.BE.domain; + +import lombok.Getter; +import lombok.Setter; + +import javax.persistence.*; +import java.util.ArrayList; +import java.util.List; + +import static javax.persistence.FetchType.LAZY; + +@Getter +@Setter +@Entity +public class Post { + + @Id + @GeneratedValue + @Column(name = "post_id") + private Long postId; + + @ManyToOne(fetch = LAZY) + @JoinColumn(name = "member_id") + private Member member; + + private String title; + + @Column(name = "contest_name") + private String contestName; + + private String deadline; + + @Column(name = "contest_award") + private Boolean ContestAward; + + @Column(name = "contest_aware_type") + private String contestAwareType; + + @Column(name = "like_count") + private Integer likeCount; + + @OneToMany(mappedBy = "post") + private List comments = new ArrayList<>(); + + @OneToMany(mappedBy = "post", cascade = CascadeType.ALL) + private List postScarps = new ArrayList<>(); + + @OneToMany(mappedBy = "post", cascade = CascadeType.ALL) + private List postLikes = new ArrayList<>(); + + @OneToMany(mappedBy = "post", cascade = CascadeType.ALL) + private List postFiles = new ArrayList<>(); + + @OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL) + @JoinColumn(name = "category_id") + private Category category; +} diff --git a/BE/src/main/java/Remoa/BE/domain/PostFile.java b/BE/src/main/java/Remoa/BE/domain/PostFile.java new file mode 100644 index 0000000..20e2c30 --- /dev/null +++ b/BE/src/main/java/Remoa/BE/domain/PostFile.java @@ -0,0 +1,36 @@ +package Remoa.BE.domain; + +import lombok.AccessLevel; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import javax.persistence.*; + +@Getter +@Setter +@Entity +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class PostFile { + + @Id + @GeneratedValue + @Column(name = "post_file_id") + private Long postFileId; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "post_id") + private Post post; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "file_id") + private UploadFile uploadFile; + + public static PostFile createPostFile(Post post, UploadFile uploadFile) { + PostFile postFile = new PostFile(); + postFile.setPost(post); + postFile.setUploadFile(uploadFile); + + return postFile; + } +} diff --git a/BE/src/main/java/Remoa/BE/domain/PostLike.java b/BE/src/main/java/Remoa/BE/domain/PostLike.java new file mode 100644 index 0000000..451b9e8 --- /dev/null +++ b/BE/src/main/java/Remoa/BE/domain/PostLike.java @@ -0,0 +1,36 @@ +package Remoa.BE.domain; + +import lombok.AccessLevel; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import javax.persistence.*; + +@Getter +@Setter +@Entity +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class PostLike { + + @Id + @GeneratedValue + @Column(name = "post_like_id") + private Long postLikeId; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "member_id") + private Member member; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "post_id") + private Post post; + + public static PostLike createPostLike(Member member, Post post) { + PostLike postLike = new PostLike(); + postLike.setPost(post); + postLike.setMember(member); + + return postLike; + } +} diff --git a/BE/src/main/java/Remoa/BE/domain/PostScarp.java b/BE/src/main/java/Remoa/BE/domain/PostScarp.java new file mode 100644 index 0000000..b0b503a --- /dev/null +++ b/BE/src/main/java/Remoa/BE/domain/PostScarp.java @@ -0,0 +1,36 @@ +package Remoa.BE.domain; + +import lombok.AccessLevel; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import javax.persistence.*; + +@Getter +@Setter +@Entity +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class PostScarp { + + @Id + @GeneratedValue + @Column(name = "post_scrap_id") + private Long postScrapId; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "member_id") + private Member member; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "post_id") + private Post post; + + public static PostScarp createPostScrap(Member member, Post post) { + PostScarp postScrap = new PostScarp(); + postScrap.setPost(post); + postScrap.setMember(member); + + return postScrap; + } +} diff --git a/BE/src/main/java/Remoa/BE/domain/Role.java b/BE/src/main/java/Remoa/BE/domain/Role.java new file mode 100644 index 0000000..6b20068 --- /dev/null +++ b/BE/src/main/java/Remoa/BE/domain/Role.java @@ -0,0 +1,13 @@ +package Remoa.BE.domain; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@AllArgsConstructor +@Getter +public enum Role { + ADMIN("ROLE_ADMIN"), + USER("ROLE_USER"); + + private String value; +} diff --git a/BE/src/main/java/Remoa/BE/domain/UploadFile.java b/BE/src/main/java/Remoa/BE/domain/UploadFile.java new file mode 100644 index 0000000..029933d --- /dev/null +++ b/BE/src/main/java/Remoa/BE/domain/UploadFile.java @@ -0,0 +1,28 @@ +package Remoa.BE.domain; + +import lombok.Getter; +import lombok.Setter; + +import javax.persistence.*; + +@Entity +@Getter +@Setter +@Table(name = "FILE") +public class UploadFile { + + @Id + @GeneratedValue + @Column(name = "file_id") + private Long uploadFileId; + + @Column(name = "original_file_name") + private String originalFileName; + + @Column(name = "save_file_name") + private String saveFileName; + + private String extension; + + //이후 업로드 날짜 및 시간, 컨텐츠 타입, 사이즈 등의 필드등이 필요할 때 손봐야할듯. +} diff --git a/BE/src/main/java/Remoa/BE/exception/CustomizedExceptionHandler.java b/BE/src/main/java/Remoa/BE/exception/CustomizedExceptionHandler.java new file mode 100644 index 0000000..3ba44c1 --- /dev/null +++ b/BE/src/main/java/Remoa/BE/exception/CustomizedExceptionHandler.java @@ -0,0 +1,58 @@ +package Remoa.BE.exception; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.validation.BindingResult; +import org.springframework.web.bind.MethodArgumentNotValidException; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +import javax.servlet.http.HttpServletRequest; +import java.time.LocalDateTime; + +@Slf4j +@RestControllerAdvice //모든 컨트롤러가 호출되기 전에 사전 실행됨. -> 이를 통해 모든 예외가 처리되는 클래스가 만들어졌다. +public class CustomizedExceptionHandler { + + @ExceptionHandler(MethodArgumentNotValidException.class) + public ResponseEntity methodValidException(MethodArgumentNotValidException e, HttpServletRequest request){ + log.warn("MethodArgumentNotValidException 발생!!! url:{}, trace:{}", request.getRequestURI(), e.getStackTrace()); + ExceptionResponse errorResponse = makeErrorResponse(e.getBindingResult()); + return new ResponseEntity<>(errorResponse, HttpStatus.BAD_REQUEST); + } + + private ExceptionResponse makeErrorResponse(BindingResult bindingResult) { + String code = ""; + String description = ""; + String detail = ""; + LocalDateTime errorOccurredTime = null; + + //에러가 있다면 + if(bindingResult.hasErrors()){ + //DTO에 설정한 meaasge값을 가져온다 + detail = bindingResult.getFieldError().getDefaultMessage(); + + errorOccurredTime = LocalDateTime.now(); + + //DTO에 유효성체크를 걸어놓은 어노테이션명을 가져온다. + String bindResultCode = bindingResult.getFieldError().getCode(); + + switch (bindResultCode){ + case "NotBlank": + code = "NotBlank Error"; + description = "비어있거나 공백일 수 없습니다."; + break; + case "Size": + code = "Size Error"; + description = "입력값의 크기가 올바르지 않습니다."; + break; + case "NotNull": + code = "NotNull Error"; + description = "필수 입력 사항입니다."; + } + } + + return new ExceptionResponse(code, description, detail, errorOccurredTime); + } +} diff --git a/BE/src/main/java/Remoa/BE/exception/ExceptionResponse.java b/BE/src/main/java/Remoa/BE/exception/ExceptionResponse.java new file mode 100644 index 0000000..f148e25 --- /dev/null +++ b/BE/src/main/java/Remoa/BE/exception/ExceptionResponse.java @@ -0,0 +1,15 @@ +package Remoa.BE.exception; + +import lombok.AllArgsConstructor; +import lombok.Data; + +import java.time.LocalDateTime; + +@Data +@AllArgsConstructor +public class ExceptionResponse { + private String code; //에러메세지 표시 + private String description; //간략 설명 + private String detail; //DTO에서 설정한 에러 메세지 값 + private LocalDateTime timestamp; //에러 발생 시간 +} diff --git a/BE/src/main/java/Remoa/BE/form/KakaoSignupForm.java b/BE/src/main/java/Remoa/BE/form/KakaoSignupForm.java new file mode 100644 index 0000000..10a320b --- /dev/null +++ b/BE/src/main/java/Remoa/BE/form/KakaoSignupForm.java @@ -0,0 +1,34 @@ +package Remoa.BE.form; + +import lombok.Getter; +import lombok.Setter; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; + +@Getter +@Setter +public class KakaoSignupForm { + + @NotBlank(message = "이메일은 필수값입니다.") + private String email; + + @NotBlank(message = "카카오에서 발급받은 id값이 누락되었습니다.") + private Long kakaoId; + + @NotBlank(message = "이름은 필수값입니다.") + private String name; + + @NotBlank(message = "생일은 필수값입니다.") + private String birth; + + @NotNull(message = "성별은 필수값입니다.") + private Boolean sex; + + @NotBlank(message = "연락처는 필수값입니다.") + private String phoneNumber; + + //필수 동의사항은 비동의시 회원가입 자체가 진행되지 않으므로 선택 동의사항에 대한 부분만 고려 + @NotNull(message = "선택 동의사항 값은 필수입니다.") + private Boolean termConsent; +} \ No newline at end of file diff --git a/BE/src/main/java/Remoa/BE/form/LoginForm.java b/BE/src/main/java/Remoa/BE/form/LoginForm.java new file mode 100644 index 0000000..4fbc7b6 --- /dev/null +++ b/BE/src/main/java/Remoa/BE/form/LoginForm.java @@ -0,0 +1,18 @@ +package Remoa.BE.form; + +import lombok.Getter; +import lombok.Setter; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.Size; + +@Getter +@Setter +public class LoginForm { + + @NotBlank(message = "이메일은 필수 입니다.") + private String email; + + @Size(min = 8, max = 20, message = "패스워드는 4~16자까지 가능합니다.") + private String password; +} diff --git a/BE/src/main/java/Remoa/BE/form/SignupForm.java b/BE/src/main/java/Remoa/BE/form/SignupForm.java new file mode 100644 index 0000000..7e510b3 --- /dev/null +++ b/BE/src/main/java/Remoa/BE/form/SignupForm.java @@ -0,0 +1,35 @@ +package Remoa.BE.form; + +import lombok.Getter; +import lombok.Setter; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; + +@Getter +@Setter +public class SignupForm { + + @NotBlank(message = "이메일은 필수값입니다.") + private String email; + + @Size(min = 8, max = 20, message = "패스워드는 4~16자까지 가능합니다.") + private String password; + + @NotBlank(message = "이름은 필수값입니다.") + private String name; + + @NotBlank(message = "생일은 필수값입니다.") + private String birth; + + @NotNull(message = "성별은 필수값입니다.") + private Boolean sex; + + @NotBlank(message = "연락처는 필수값입니다.") + private String phoneNumber; + + //필수 동의사항은 비동의시 회원가입 자체가 진행되지 않으므로 선택 동의사항에 대한 부분만 고려 + @NotNull(message = "선택 동의사항 값은 필수입니다.") + private Boolean termConsent; +} \ No newline at end of file diff --git a/BE/src/main/java/Remoa/BE/repository/CategoryRepository.java b/BE/src/main/java/Remoa/BE/repository/CategoryRepository.java new file mode 100644 index 0000000..9b0e5ce --- /dev/null +++ b/BE/src/main/java/Remoa/BE/repository/CategoryRepository.java @@ -0,0 +1,56 @@ +package Remoa.BE.repository; + +import Remoa.BE.domain.Category; +import Remoa.BE.domain.Member; +import Remoa.BE.domain.MemberCategory; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Repository; + +import javax.persistence.EntityManager; +import javax.persistence.TypedQuery; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +@Repository +@RequiredArgsConstructor +public class CategoryRepository { + + private final EntityManager em; + + public void saveCategory(Category category) { + em.persist(category); + } + + public List findAllCategories() { + return em.createQuery("select c from Category c", Category.class) + .getResultList(); + } + + public Category findByCategoryName(String categoryName) { + Optional category = em.createQuery("select c from Category c " + + "where c.name = :categoryName", Category.class) + .setParameter("categoryName", categoryName) + .getResultStream() + .findAny(); + + if (category.isPresent()) { + return category.get(); + } else { + throw new RuntimeException("해당 카테고리는 존재하지 않습니다."); + } + } + + public List findOnesCategories(Member member) { + List memberCategories = em.createQuery("select mc from MemberCategory mc " + + "where mc.member = :member", MemberCategory.class) + .setParameter("member", member) + .getResultList(); + + List categories = new ArrayList<>(); + for (MemberCategory memberCategory : memberCategories) { + categories.add(memberCategory.getCategory()); + } + return categories; + } +} diff --git a/BE/src/main/java/Remoa/BE/repository/CommentRepository.java b/BE/src/main/java/Remoa/BE/repository/CommentRepository.java new file mode 100644 index 0000000..c9452ff --- /dev/null +++ b/BE/src/main/java/Remoa/BE/repository/CommentRepository.java @@ -0,0 +1,86 @@ +package Remoa.BE.repository; + +import Remoa.BE.domain.*; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Repository; + +import javax.persistence.EntityManager; +import java.util.List; +import java.util.Optional; + +@Slf4j +@Repository +@RequiredArgsConstructor +public class CommentRepository { + + private final EntityManager em; + + public void saveComment(Comment comment) { + em.persist(comment); + } + + public Comment findByCommentId(Long commentId) { + return em.find(Comment.class, commentId); + } + + /** + * 포스트 별 댓글을 찾아오기 위한 메서드 + * @param post + * @return List + */ + public List findByPost(Post post) { + return em.createQuery("select c from Comment c where c.post = :post", Comment.class) + .setParameter("post", post) + .getResultList(); + } + + public void saveCommentLike(CommentLike commentLike) { + em.persist(commentLike); + } + + /** + * commentLikeAction 메서드를 실행하기 전 이미 해당 댓글에 대한 좋아요를 했는지 검증->service 단에서 return 값의 null이면 좋아요 가능. + * 혹은 좋아요 취소를 위해 사용할 수도 있다. + */ + public Optional findMemberCommendLike(Member member, Comment comment) { + return em.createQuery("select cl from CommentLike cl " + + "where cl.comment = :comment and cl.member = :member", CommentLike.class) + .setParameter("comment", comment) + .setParameter("member", member) + .getResultStream() + .findAny(); + } + + public Integer findCommentLike(Comment comment) { + return em.createQuery("select cl from CommentLike cl where cl.comment = :comment", CommentLike.class) + .setParameter("comment", comment) + .getResultList() + .size(); + } + + public void saveCommentBookmark(CommentBookmark commentBookmark) { + em.persist(commentBookmark); + } + + /** + * commentBookmarkAction 메서드를 실행하기 전 이미 해당 댓글에 대한 북마크를 했는지 검증->service 단에서 return 값의 null이면 북마크 가능. + * 혹은 북마크 해제를 위해 사용할 수도 있다. + */ + public Optional findMemberCommendBookmark(Member member, Comment comment) { + return em.createQuery("select cb from CommentBookmark cb " + + "where cb.comment = :comment and cb.member = :member", CommentBookmark.class) + .setParameter("comment", comment) + .setParameter("member", member) + .getResultStream() + .findAny(); + } + + /* //필요없을 거 같아서 주석처리... + public Integer findCommentBookmark(Comment comment) { + return em.createQuery("select cb from CommentBookmark cb where cb.comment = :comment", CommentBookmark.class) + .setParameter("comment", comment) + .getResultList() + .size(); + }*/ +} diff --git a/BE/src/main/java/Remoa/BE/repository/MemberRepository.java b/BE/src/main/java/Remoa/BE/repository/MemberRepository.java new file mode 100644 index 0000000..afb74b8 --- /dev/null +++ b/BE/src/main/java/Remoa/BE/repository/MemberRepository.java @@ -0,0 +1,64 @@ +package Remoa.BE.repository; + +import Remoa.BE.domain.Follow; +import Remoa.BE.domain.Member; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Repository; + +import javax.persistence.EntityManager; +import java.util.List; +import java.util.Optional; + +@Repository +@Slf4j +@RequiredArgsConstructor +public class MemberRepository { + public final EntityManager em; + + public void save(Member member) { + this.em.persist(member); + } + + public Member findOne(Long id) { + return em.find(Member.class, id); + } + + public List findAll() { + return this.em.createQuery("select m from Member m", Member.class).getResultList(); + } + + public List findByEmail(String email) { + return em.createQuery("select m from Member m where m.email = :email", Member.class) + .setParameter("email", email) + .getResultList(); + } + + public Optional findByKakaoId(Long kakaoId) { + return this.em.createQuery("select k from Member k where k.kakaoId = :kakaoId", Member.class) + .setParameter("kakaoId", kakaoId) + .getResultStream() + .findAny(); + } + + public void follow(Follow follow) { + em.persist(follow); + } + + /** + * 팔로우 여부를 두 멤버 객체를 받아와서 db에 검색 후 List로 받아서 List에 값이 있으면 팔로우가 되어있는 상태, 없으면 팔로우 되어있지 않은 상태. + * 팔로우/언팔로우 기능 전에 이 메서드를 통해 이후 동작을 정할 수 있다. + * @param fromMember + * @param toMember + * @return 팔로우 되어있지 않음 : false, 팔로우 되어있음 : true + */ + public Boolean isFollow(Member fromMember, Member toMember) { + return !em.createQuery("select f from Follow f " + + "where f.fromMember = :fromMember and f.toMemberId = :toMemberId", Follow.class) + .setParameter("fromMember", fromMember) + .setParameter("toMemberId", toMember.getMemberId()) + .getResultList() + .isEmpty(); + } + +} diff --git a/BE/src/main/java/Remoa/BE/repository/PostRepository.java b/BE/src/main/java/Remoa/BE/repository/PostRepository.java new file mode 100644 index 0000000..bbc6132 --- /dev/null +++ b/BE/src/main/java/Remoa/BE/repository/PostRepository.java @@ -0,0 +1,62 @@ +package Remoa.BE.repository; + +import Remoa.BE.domain.*; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Repository; + +import javax.persistence.EntityManager; +import java.util.List; +import java.util.Optional; + +@Slf4j +@Repository +@RequiredArgsConstructor +public class PostRepository { + + private final EntityManager em; + + public void savePost(Post post) { + em.persist(post); + } + + public Post findByPostId(Long postId) { + return em.find(Post.class, postId); + } + + public Optional findByMemberId(Member member) { + return em.createQuery("select p from Post p where p.member = :member", Post.class) + .setParameter("member", member) + .getResultStream() + .findAny(); + } + + public void savePostScrap(PostScarp postScarp) { + em.persist(postScarp); + } + + public Post findScrapedPost(Member member) { + return em.createQuery("select ps from PostScarp ps where ps.member = :member", PostScarp.class) + .setParameter("member", member) + .getResultStream() + .findAny() + .get() + .getPost(); + } + + public Post findLikedPost(Member member) { + return em.createQuery("select pl from PostLike pl where pl.member = :member", PostLike.class) + .setParameter("member", member) + .getResultStream() + .findAny() + .get() + .getPost(); + } + + public List findPostsByCategory(Category category) { + return em.createQuery("select p from Post p where p.category = :category", Post.class) + .setParameter("category", category) + .getResultList(); + } + +} diff --git a/BE/src/main/java/Remoa/BE/repository/UploadFileRepository.java b/BE/src/main/java/Remoa/BE/repository/UploadFileRepository.java new file mode 100644 index 0000000..863dd74 --- /dev/null +++ b/BE/src/main/java/Remoa/BE/repository/UploadFileRepository.java @@ -0,0 +1,18 @@ +package Remoa.BE.repository; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Repository; + +import javax.persistence.EntityManager; + +@Repository +@RequiredArgsConstructor +@Slf4j +public class UploadFileRepository { + + private final EntityManager em; + + + +} diff --git a/BE/src/main/java/Remoa/BE/service/CategoryService.java b/BE/src/main/java/Remoa/BE/service/CategoryService.java new file mode 100644 index 0000000..22fe806 --- /dev/null +++ b/BE/src/main/java/Remoa/BE/service/CategoryService.java @@ -0,0 +1,52 @@ +package Remoa.BE.service; + +import Remoa.BE.domain.Category; +import Remoa.BE.domain.Member; +import Remoa.BE.domain.MemberCategory; +import Remoa.BE.repository.CategoryRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import org.springframework.transaction.annotation.Transactional; + +import java.util.ArrayList; +import java.util.List; + +@Service +@Transactional +@RequiredArgsConstructor +public class CategoryService { + + private final CategoryRepository categoryRepository; + + public void persistCategory(Category... categories) { + + for (Category category : categories) { + categoryRepository.saveCategory(category); + } + } + + @Transactional + public List setPreferCategory(Member member, String... categoryNames) { + + for (String categoryName : categoryNames) { + Category category = categoryRepository.findByCategoryName(categoryName); + MemberCategory memberCategory = MemberCategory.createMemberCategory(member, category); + member.addMemberCategory(memberCategory); + } + + List categories = new ArrayList<>(); + for(MemberCategory memberCategory : member.getMemberCategories()){ + categories.add(memberCategory.getCategory()); + } + return categories; + } + + public List findMemberCategory(Member member) { + return categoryRepository.findOnesCategories(member); + } + + public List findAllCategories() { + return categoryRepository.findAllCategories(); + } +} \ No newline at end of file diff --git a/BE/src/main/java/Remoa/BE/service/CommentService.java b/BE/src/main/java/Remoa/BE/service/CommentService.java new file mode 100644 index 0000000..a02d87d --- /dev/null +++ b/BE/src/main/java/Remoa/BE/service/CommentService.java @@ -0,0 +1,41 @@ +package Remoa.BE.service; + +import Remoa.BE.domain.*; +import Remoa.BE.repository.CommentRepository; +import Remoa.BE.repository.PostRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + +@Service +@Transactional +@RequiredArgsConstructor +public class CommentService { + + private final CommentRepository commentRepository; + private final PostRepository postRepository; + + public Long writeComment(Comment comment) { + commentRepository.saveComment(comment); + return comment.getCommentId(); + } + + public List loadCommentsByPostId(Long postId) { + Post post = postRepository.findByPostId(postId); + return commentRepository.findByPost(post); + } + + public Long commentLikeAction(Comment comment, Member member) { + CommentLike commentLike = CommentLike.createCommentLike(member, comment); + commentRepository.saveCommentLike(commentLike); + return commentLike.getCommentLikeId(); + } + + public Long commentBookmarkAction(Comment comment, Member member) { + CommentBookmark commentBookmark = CommentBookmark.createCommentBookmark(member, comment); + commentRepository.saveCommentBookmark(commentBookmark); + return commentBookmark.getCommentBookmarkId(); + } +} diff --git a/BE/src/main/java/Remoa/BE/service/FileService.java b/BE/src/main/java/Remoa/BE/service/FileService.java new file mode 100644 index 0000000..ca7694b --- /dev/null +++ b/BE/src/main/java/Remoa/BE/service/FileService.java @@ -0,0 +1,26 @@ +package Remoa.BE.service; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; + +@Service +public class FileService { + + @Value("${uploadFolder}") + private String uploadFolder; + + public void save(MultipartFile file, String saveFileName) { + Path upload = Paths.get(uploadFolder); + try { + Files.copy(file.getInputStream(), upload.resolve(saveFileName)); + } catch (IOException e) { + e.printStackTrace(); + } + } +} diff --git a/BE/src/main/java/Remoa/BE/service/KakaoService.java b/BE/src/main/java/Remoa/BE/service/KakaoService.java new file mode 100644 index 0000000..cd6393c --- /dev/null +++ b/BE/src/main/java/Remoa/BE/service/KakaoService.java @@ -0,0 +1,167 @@ +package Remoa.BE.service; + +import Remoa.BE.domain.Member; +import Remoa.BE.repository.MemberRepository; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.json.simple.JSONObject; +import org.json.simple.parser.JSONParser; +import org.json.simple.parser.ParseException; +import org.springframework.stereotype.Service; + +import java.io.*; +import java.net.HttpURLConnection; +import java.net.MalformedURLException; +import java.net.ProtocolException; +import java.net.URL; +import java.util.HashMap; +import java.util.Map; + +@Service +@Slf4j +@RequiredArgsConstructor +public class KakaoService { + + //카카오 로그인시 접속해야 할 링크 : https://kauth.kakao.com/oauth/authorize?client_id=139febf9e13da4d124d1c1faafcf3f86&redirect_uri=http://localhost:8080/login/kakao&response_type=code + + private final MemberRepository memberRepository; + + public String getToken(String code) throws IOException { + + String host = "https://kauth.kakao.com/oauth/token"; + URL url = new URL(host); + HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection(); + String token = ""; + + try { + urlConnection.setRequestMethod("POST"); + urlConnection.setDoOutput(true); // OutputStream으로 POST 데이터를 넘겨주겠다는 옵션 + + BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(urlConnection.getOutputStream())); + StringBuilder sb = new StringBuilder(); + sb.append("grant_type=authorization_code"); + sb.append("&client_id=139febf9e13da4d124d1c1faafcf3f86"); + sb.append("&redirect_uri=http://localhost:8080/login/kakao"); + sb.append("&code=" + code); + sb.append("&client_secret=5IueqXws75WoH1e3gCSI2aNxQgOGMdBG"); + + bw.write(sb.toString()); + + bw.flush(); + + int responseCode = urlConnection.getResponseCode(); + log.debug("responseCode = {}", responseCode); + + BufferedReader br = new BufferedReader(new InputStreamReader(urlConnection.getInputStream())); + String line = ""; + String result = ""; + while ((line = br.readLine()) != null) { + result += line; + } + + // json parsing + JSONParser parser = new JSONParser(); + JSONObject elem = (JSONObject) parser.parse(result); + + String access_token = elem.get("access_token").toString(); + String refresh_token = elem.get("refresh_token").toString(); + + token = access_token; + + br.close(); + bw.close(); + } catch (IOException e) { + e.printStackTrace(); + } catch (ParseException e) { + e.printStackTrace(); + } + + + return token; + } + + public Map getUserInfo(String access_token) throws IOException { + String host = "https://kapi.kakao.com/v2/user/me"; + Map result = new HashMap<>(); + try { + URL url = new URL(host); + + HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection(); + urlConnection.setRequestProperty("Authorization", "Bearer " + access_token); + urlConnection.setRequestMethod("GET"); + + int responseCode = urlConnection.getResponseCode(); + log.debug("responseCode = {}", responseCode); + + BufferedReader br = new BufferedReader(new InputStreamReader(urlConnection.getInputStream())); + String line = ""; + String res = ""; + while((line=br.readLine())!=null) + { + res+=line; + } + + JSONParser parser = new JSONParser(); + JSONObject obj = (JSONObject) parser.parse(res); + /*JSONObject kakao_account = (JSONObject) obj.get("kakao_account");*/ + JSONObject properties = (JSONObject) obj.get("properties"); + + String id = obj.get("id").toString(); + String nickname = properties.get("nickname").toString(); + + result.put("id", id); + result.put("nickname", nickname); + + br.close(); + + + } catch (IOException | ParseException e) { + e.printStackTrace(); + } + + return result; + } + + public String getAgreementInfo(String access_token) + { + String result = ""; + String host = "https://kapi.kakao.com/v2/user/scopes"; + try{ + URL url = new URL(host); + HttpURLConnection urlConnection = (HttpURLConnection)url.openConnection(); + urlConnection.setRequestMethod("GET"); + urlConnection.setRequestProperty("Authorization", "Bearer " + access_token); + + BufferedReader br = new BufferedReader(new InputStreamReader(urlConnection.getInputStream())); + String line = ""; + while((line=br.readLine())!=null) + { + result += line; + } + + int responseCode = urlConnection.getResponseCode(); + log.debug("responseCode = {}", responseCode); + + // result는 json 포멧. + br.close(); + + } catch (MalformedURLException e) { + e.printStackTrace(); + } catch (ProtocolException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + return result; + } + + public Member distinguishKakaoId(Long kakaoId) { + + if (!memberRepository.findByKakaoId(kakaoId).isPresent()) { + return null; + } + Member kakaoMember = memberRepository.findByKakaoId(kakaoId).get(); + + return kakaoMember; + } +} diff --git a/BE/src/main/java/Remoa/BE/service/LoginService.java b/BE/src/main/java/Remoa/BE/service/LoginService.java new file mode 100644 index 0000000..1e0803f --- /dev/null +++ b/BE/src/main/java/Remoa/BE/service/LoginService.java @@ -0,0 +1,34 @@ +package Remoa.BE.service; + +import Remoa.BE.domain.Member; +import Remoa.BE.repository.MemberRepository; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.context.SecurityContext; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.security.web.context.HttpSessionSecurityContextRepository; +import org.springframework.stereotype.Service; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpSession; +import java.util.ArrayList; +import java.util.List; + +@Slf4j +@Service +@RequiredArgsConstructor +public class LoginService { + + private final MemberRepository memberRepository; + private final PasswordEncoder bCryptPasswordEncoder; + + public Member login(String email, String password) { + return memberRepository.findByEmail(email) + .stream().filter(member -> bCryptPasswordEncoder.matches(password, member.getPassword())).findAny().orElse(null); + } + +} \ No newline at end of file diff --git a/BE/src/main/java/Remoa/BE/service/SignupService.java b/BE/src/main/java/Remoa/BE/service/SignupService.java new file mode 100644 index 0000000..20043e5 --- /dev/null +++ b/BE/src/main/java/Remoa/BE/service/SignupService.java @@ -0,0 +1,45 @@ +package Remoa.BE.service; + +import Remoa.BE.domain.Member; +import Remoa.BE.repository.MemberRepository; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + +@Service +@Transactional(readOnly = true) +@Slf4j +@RequiredArgsConstructor +public class SignupService { + private final MemberRepository memberRepository; + private final PasswordEncoder bCryptPasswordEncoder; + + @Transactional + public Long join(Member member) { + this.validateDuplicateMember(member); + member.hashPassword(this.bCryptPasswordEncoder); + member.setRole("USER"); + this.memberRepository.save(member); + return member.getMemberId(); + } + + private void validateDuplicateMember(Member member) { + log.info("member={}", member.getEmail()); + List findMembers = this.memberRepository.findByEmail(member.getEmail()); + if (!findMembers.isEmpty()) { + throw new IllegalStateException("이미 존재하는 회원입니다."); + } + } + + public Member findOne(Long memberId) { + return this.memberRepository.findOne(memberId); + } + + public Boolean isAdminExist() { + return !memberRepository.findByEmail("spparta@gmail.com").isEmpty(); + } +} diff --git a/BE/src/main/resources/application.yml b/BE/src/main/resources/application.yml new file mode 100644 index 0000000..cd836d0 --- /dev/null +++ b/BE/src/main/resources/application.yml @@ -0,0 +1,37 @@ + +spring: + + datasource: + url: jdbc:mysql://3.39.191.77:3306/remoa?useSSL=true&characterEncoding=UTF-8 + driver-class-name: com.mysql.cj.jdbc.Driver + username: ex2 + password: 1234 + + jpa: + database-platform: org.hibernate.dialect.MySQL5InnoDBDialect + hibernate: + ddl-auto: create + properties: + hibernate: + format_sql: true + show_sql: true + defer-datasource-initialization: true + + init: + sql: + mode: always + +#파일 업/다운로드 관련- 230126 추가 + servlet: + multipart: + enabled: true + file-size-threshold: 2KB + max-file-size: 200MB + max-request-size: 200MB + +logging: + level: + org.hibernate.SQL: debug + org.hibernate.type: trace + +uploadFolder: upload \ No newline at end of file diff --git a/BE/src/test/java/Remoa/BE/BeApplicationTests.java b/BE/src/test/java/Remoa/BE/BeApplicationTests.java new file mode 100644 index 0000000..f17a6a7 --- /dev/null +++ b/BE/src/test/java/Remoa/BE/BeApplicationTests.java @@ -0,0 +1,13 @@ +package Remoa.BE; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +class BeApplicationTests { + + @Test + void contextLoads() { + } + +} diff --git a/BE/src/test/java/Remoa/BE/category/CategoryTest.java b/BE/src/test/java/Remoa/BE/category/CategoryTest.java new file mode 100644 index 0000000..2346595 --- /dev/null +++ b/BE/src/test/java/Remoa/BE/category/CategoryTest.java @@ -0,0 +1,69 @@ +package Remoa.BE.category; + +import Remoa.BE.domain.Category; +import Remoa.BE.domain.Member; +import Remoa.BE.domain.MemberCategory; +import Remoa.BE.repository.MemberRepository; +import Remoa.BE.service.CategoryService; +import Remoa.BE.service.SignupService; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.transaction.annotation.Transactional; + +import java.util.ArrayList; +import java.util.List; + +import static org.junit.Assert.assertEquals; + +@RunWith(SpringRunner.class) +@SpringBootTest +@Transactional +public class CategoryTest { + + @Autowired + CategoryService categoryService; + + @Autowired + SignupService signupService; + + @Autowired + MemberRepository memberRepository; + + @Test + public void 카테고리_테스트() throws Exception { + //given + Member testMember = createMember(); + signupService.join(testMember); + + String category1 = "idea"; + String category2 = "video"; + + //when + //testMember에 대한 카테고리 세팅 + List testMemberCategories = categoryService.setPreferCategory(testMember, category1, category2); + + + //email로 testMember를 찾은 후 category 찾아오기 + Member findMember = memberRepository.findByEmail("tester@test.com").get(0); + List findMemberCategories = categoryService.findMemberCategory(findMember); + + //then + assertEquals("하나의 멤버에 설정한 카테고리와 찾은 카테고리는 같아야한다.", testMemberCategories, findMemberCategories); + } + + private Member createMember() { + Member member = new Member(); + member.setEmail("tester@test.com"); + member.setName("test"); + member.setPassword("test"); + member.setBirth("20101010"); + member.setSex(false); + member.setPhoneNumber("12341234"); + member.setTermConsent(true); + + return member; + } +} diff --git a/BE/test b/BE/test deleted file mode 100644 index 9c558e3..0000000 --- a/BE/test +++ /dev/null @@ -1 +0,0 @@ -. diff --git a/FE/.env b/FE/.env new file mode 100644 index 0000000..44b6a02 --- /dev/null +++ b/FE/.env @@ -0,0 +1 @@ +REACT_APP_REST_API_KEY=139febf9e13da4d124d1c1faafcf3f86 \ No newline at end of file diff --git a/FE/.gitignore b/FE/.gitignore index 4d29575..9a0d798 100644 --- a/FE/.gitignore +++ b/FE/.gitignore @@ -4,6 +4,8 @@ /node_modules /.pnp .pnp.js +.env +package.json # testing /coverage @@ -21,3 +23,5 @@ npm-debug.log* yarn-debug.log* yarn-error.log* + +.env \ No newline at end of file diff --git a/FE/package-lock.json b/FE/package-lock.json index 15f0803..3a7d4d9 100644 --- a/FE/package-lock.json +++ b/FE/package-lock.json @@ -12,17 +12,18 @@ "@emotion/react": "^11.10.5", "@emotion/styled": "^11.10.5", "@material-ui/core": "^4.12.4", + "@material-ui/icons": "^4.11.3", "@mui/icons-material": "^5.11.0", - "@mui/material": "^5.11.3", + "@mui/material": "^5.11.4", "@testing-library/jest-dom": "^5.16.5", "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^13.5.0", - "antd": "^5.0.7", "axios": "^1.2.1", "bootstrap": "^5.2.3", "react": "^18.2.0", "react-bootstrap": "^2.7.0", "react-dom": "^18.2.0", + "react-kakao-login": "^2.1.1", "react-router-dom": "^6.4.5", "react-scripts": "5.0.1", "styled-components": "^5.3.6", @@ -46,71 +47,6 @@ "node": ">=6.0.0" } }, - "node_modules/@ant-design/colors": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/@ant-design/colors/-/colors-6.0.0.tgz", - "integrity": "sha512-qAZRvPzfdWHtfameEGP2Qvuf838NhergR35o+EuVyB5XvSA98xod5r4utvi4TJ3ywmevm290g9nsCG5MryrdWQ==", - "dependencies": { - "@ctrl/tinycolor": "^3.4.0" - } - }, - "node_modules/@ant-design/cssinjs": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@ant-design/cssinjs/-/cssinjs-1.4.0.tgz", - "integrity": "sha512-siFyX5VyiJWKBQAffI8zE0WZ4OWfI35QnqesbtHaVg49QxHFAcFN9VbJ+Qoos24JxABcxfopEwMWe2joZ2QVBQ==", - "dependencies": { - "@babel/runtime": "^7.11.1", - "@emotion/hash": "^0.8.0", - "@emotion/unitless": "^0.7.5", - "classnames": "^2.3.1", - "csstype": "^3.0.10", - "rc-util": "^5.27.0", - "stylis": "^4.0.13" - }, - "peerDependencies": { - "react": ">=16.0.0", - "react-dom": ">=16.0.0" - } - }, - "node_modules/@ant-design/icons": { - "version": "4.8.0", - "resolved": "https://registry.npmjs.org/@ant-design/icons/-/icons-4.8.0.tgz", - "integrity": "sha512-T89P2jG2vM7OJ0IfGx2+9FC5sQjtTzRSz+mCHTXkFn/ELZc2YpfStmYHmqzq2Jx55J0F7+O6i5/ZKFSVNWCKNg==", - "dependencies": { - "@ant-design/colors": "^6.0.0", - "@ant-design/icons-svg": "^4.2.1", - "@babel/runtime": "^7.11.2", - "classnames": "^2.2.6", - "rc-util": "^5.9.4" - }, - "engines": { - "node": ">=8" - }, - "peerDependencies": { - "react": ">=16.0.0", - "react-dom": ">=16.0.0" - } - }, - "node_modules/@ant-design/icons-svg": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/@ant-design/icons-svg/-/icons-svg-4.2.1.tgz", - "integrity": "sha512-EB0iwlKDGpG93hW8f85CTJTs4SvMX7tt5ceupvhALp1IF44SeUFOMhKUOYqpsoYWQKAOuTRDMqn75rEaKDp0Xw==" - }, - "node_modules/@ant-design/react-slick": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@ant-design/react-slick/-/react-slick-1.0.0.tgz", - "integrity": "sha512-OKxZsn8TAf8fYxP79rDXgLs9zvKMTslK6dJ4iLhDXOujUqC5zJPBRszyrcEHXcMPOm1Sgk40JgyF3yiL/Swd7w==", - "dependencies": { - "@babel/runtime": "^7.10.4", - "classnames": "^2.2.5", - "json2mq": "^0.2.0", - "resize-observer-polyfill": "^1.5.1", - "throttle-debounce": "^5.0.0" - }, - "peerDependencies": { - "react": ">=16.9.0" - } - }, "node_modules/@babel/code-frame": { "version": "7.18.6", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", @@ -2277,14 +2213,6 @@ "postcss-selector-parser": "^6.0.10" } }, - "node_modules/@ctrl/tinycolor": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/@ctrl/tinycolor/-/tinycolor-3.5.0.tgz", - "integrity": "sha512-tlJpwF40DEQcfR/QF+wNMVyGMaO9FQp6Z1Wahj4Gk3CJQYHwA2xVG7iKDFdW6zuxZY9XWOpGcfNCTsX4McOsOg==", - "engines": { - "node": ">=10" - } - }, "node_modules/@emotion/babel-plugin": { "version": "11.10.5", "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.10.5.tgz", @@ -2307,11 +2235,6 @@ "@babel/core": "^7.0.0" } }, - "node_modules/@emotion/babel-plugin/node_modules/@emotion/hash": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.0.tgz", - "integrity": "sha512-14FtKiHhy2QoPIzdTcvh//8OyBlknNs2nXRwIhG904opCby3l+9Xaf/wuPvICBF0rc1ZCNBd3nKe9cd2mecVkQ==" - }, "node_modules/@emotion/babel-plugin/node_modules/source-map": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", @@ -2333,9 +2256,9 @@ } }, "node_modules/@emotion/hash": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.8.0.tgz", - "integrity": "sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==" + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.0.tgz", + "integrity": "sha512-14FtKiHhy2QoPIzdTcvh//8OyBlknNs2nXRwIhG904opCby3l+9Xaf/wuPvICBF0rc1ZCNBd3nKe9cd2mecVkQ==" }, "node_modules/@emotion/is-prop-valid": { "version": "1.2.0", @@ -2389,11 +2312,6 @@ "csstype": "^3.0.2" } }, - "node_modules/@emotion/serialize/node_modules/@emotion/hash": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.0.tgz", - "integrity": "sha512-14FtKiHhy2QoPIzdTcvh//8OyBlknNs2nXRwIhG904opCby3l+9Xaf/wuPvICBF0rc1ZCNBd3nKe9cd2mecVkQ==" - }, "node_modules/@emotion/serialize/node_modules/@emotion/unitless": { "version": "0.8.0", "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.8.0.tgz", @@ -3410,12 +3328,12 @@ } } }, - "node_modules/@material-ui/core/node_modules/react-is": { - "version": "17.0.2", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", - "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" + "node_modules/@material-ui/core/node_modules/@emotion/hash": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.8.0.tgz", + "integrity": "sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==" }, - "node_modules/@material-ui/styles": { + "node_modules/@material-ui/core/node_modules/@material-ui/styles": { "version": "4.11.5", "resolved": "https://registry.npmjs.org/@material-ui/styles/-/styles-4.11.5.tgz", "integrity": "sha512-o/41ot5JJiUsIETME9wVLAJrmIWL3j0R0Bj2kCOLbSfqEkKf0fmaPt+5vtblUh5eXr2S+J/8J3DaCb10+CzPGA==", @@ -3456,12 +3374,7 @@ } } }, - "node_modules/@material-ui/styles/node_modules/csstype": { - "version": "2.6.21", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.21.tgz", - "integrity": "sha512-Z1PhmomIfypOpoMjRQB70jfvy/wxT50qW08YXO5lMIJkrdq4yOTR+AW7FqutScmB9NkLwxo+jU+kZLbofZZq/w==" - }, - "node_modules/@material-ui/system": { + "node_modules/@material-ui/core/node_modules/@material-ui/system": { "version": "4.12.2", "resolved": "https://registry.npmjs.org/@material-ui/system/-/system-4.12.2.tgz", "integrity": "sha512-6CSKu2MtmiJgcCGf6nBQpM8fLkuB9F55EKfbdTC80NND5wpTmKzwdhLYLH3zL4cLlK0gVaaltW7/wMuyTnN0Lw==", @@ -3489,25 +3402,7 @@ } } }, - "node_modules/@material-ui/system/node_modules/csstype": { - "version": "2.6.21", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.21.tgz", - "integrity": "sha512-Z1PhmomIfypOpoMjRQB70jfvy/wxT50qW08YXO5lMIJkrdq4yOTR+AW7FqutScmB9NkLwxo+jU+kZLbofZZq/w==" - }, - "node_modules/@material-ui/types": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/@material-ui/types/-/types-5.1.0.tgz", - "integrity": "sha512-7cqRjrY50b8QzRSYyhSpx4WRw2YuO0KKIGQEVk5J8uoz2BanawykgZGoWEqKm7pVIbzFDN0SpPcVV4IhOFkl8A==", - "peerDependencies": { - "@types/react": "*" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@material-ui/utils": { + "node_modules/@material-ui/core/node_modules/@material-ui/utils": { "version": "4.11.3", "resolved": "https://registry.npmjs.org/@material-ui/utils/-/utils-4.11.3.tgz", "integrity": "sha512-ZuQPV4rBK/V1j2dIkSSEcH5uT6AaHuKWFfotADHsC0wVL1NLd2WkFCm4ZZbX33iO4ydl6V0GPngKm8HZQ2oujg==", @@ -3524,15 +3419,55 @@ "react-dom": "^16.8.0 || ^17.0.0" } }, - "node_modules/@material-ui/utils/node_modules/react-is": { + "node_modules/@material-ui/core/node_modules/csstype": { + "version": "2.6.21", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.21.tgz", + "integrity": "sha512-Z1PhmomIfypOpoMjRQB70jfvy/wxT50qW08YXO5lMIJkrdq4yOTR+AW7FqutScmB9NkLwxo+jU+kZLbofZZq/w==" + }, + "node_modules/@material-ui/core/node_modules/react-is": { "version": "17.0.2", "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" }, + "node_modules/@material-ui/icons": { + "version": "4.11.3", + "resolved": "https://registry.npmjs.org/@material-ui/icons/-/icons-4.11.3.tgz", + "integrity": "sha512-IKHlyx6LDh8n19vzwH5RtHIOHl9Tu90aAAxcbWME6kp4dmvODM3UvOHJeMIDzUbd4muuJKHmlNoBN+mDY4XkBA==", + "dependencies": { + "@babel/runtime": "^7.4.4" + }, + "engines": { + "node": ">=8.0.0" + }, + "peerDependencies": { + "@material-ui/core": "^4.0.0", + "@types/react": "^16.8.6 || ^17.0.0", + "react": "^16.8.0 || ^17.0.0", + "react-dom": "^16.8.0 || ^17.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@material-ui/types": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@material-ui/types/-/types-5.1.0.tgz", + "integrity": "sha512-7cqRjrY50b8QzRSYyhSpx4WRw2YuO0KKIGQEVk5J8uoz2BanawykgZGoWEqKm7pVIbzFDN0SpPcVV4IhOFkl8A==", + "peerDependencies": { + "@types/react": "*" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@mui/base": { - "version": "5.0.0-alpha.112", - "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-alpha.112.tgz", - "integrity": "sha512-KPwb1iYPXsV/P8uu0SNQrj7v7YU6wdN4Eccc2lZQyRDW+f6PJYjHBuFUTYKc408B98Jvs1XbC/z5MN45a2DWrQ==", + "version": "5.0.0-alpha.113", + "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-alpha.113.tgz", + "integrity": "sha512-XSjvyQWATM8uk+EJZvYna8D21kOXC42lwb3q4K70btuGieKlCIQLaHTTDV2OfD4+JfT4o3NJy3I4Td2co31RZA==", "dependencies": { "@babel/runtime": "^7.20.7", "@emotion/is-prop-valid": "^1.2.0", @@ -3562,9 +3497,9 @@ } }, "node_modules/@mui/core-downloads-tracker": { - "version": "5.11.3", - "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-5.11.3.tgz", - "integrity": "sha512-Bgb6//KtxY7IC7M5Pa5RKFX1wttc213mqpKqydnz3wn4BGDXfA5c0vf5nTu5zqsJeB4T3ysAJTRJhQ/E1GsZDQ==", + "version": "5.11.4", + "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-5.11.4.tgz", + "integrity": "sha512-jWVwGM3vG4O0sXcW0VcIl+njCWbGCBF5vvjRpuKJajrz51AD7D6+fP1SkInZXVk5pRHf6Bnk/Yj9Of9gXxb/hA==", "funding": { "type": "opencollective", "url": "https://opencollective.com/mui" @@ -3596,14 +3531,14 @@ } }, "node_modules/@mui/material": { - "version": "5.11.3", - "resolved": "https://registry.npmjs.org/@mui/material/-/material-5.11.3.tgz", - "integrity": "sha512-Oz+rMFiMtxzzDLUxKyyj4mSxF9ShmsBoJ9qvglXCYqklgSrEl1R/Z4hfPZ+2pWd5CriO8U/0CFHr4DksrlTiCw==", + "version": "5.11.4", + "resolved": "https://registry.npmjs.org/@mui/material/-/material-5.11.4.tgz", + "integrity": "sha512-ZL/czK9ynrQJ6uyDwQgK+j7m1iKA1XKPON+rEPupwAu/bJ1XJxD+H/H2bkMM8UpOkzaucx/WuMbJJGQ60l7gBg==", "dependencies": { "@babel/runtime": "^7.20.7", - "@mui/base": "5.0.0-alpha.112", - "@mui/core-downloads-tracker": "^5.11.3", - "@mui/system": "^5.11.2", + "@mui/base": "5.0.0-alpha.113", + "@mui/core-downloads-tracker": "^5.11.4", + "@mui/system": "^5.11.4", "@mui/types": "^7.2.3", "@mui/utils": "^5.11.2", "@types/react-transition-group": "^4.4.5", @@ -3697,9 +3632,9 @@ } }, "node_modules/@mui/system": { - "version": "5.11.2", - "resolved": "https://registry.npmjs.org/@mui/system/-/system-5.11.2.tgz", - "integrity": "sha512-PPkYhrcP2MkhscX6SauIl0wPgra0w1LGPtll+hIKc2Z2JbGRSrUCFif93kxejB7I1cAoCay9jWW4mnNhsOqF/g==", + "version": "5.11.4", + "resolved": "https://registry.npmjs.org/@mui/system/-/system-5.11.4.tgz", + "integrity": "sha512-fE2Ts33V5zh7ouciwXgMm/a6sLvjIj9OMeojuHNYY7BStTxparC/Fp9CNUZNJwt76U6ZJC59aYScFSRQKbW08g==", "dependencies": { "@babel/runtime": "^7.20.7", "@mui/private-theming": "^5.11.2", @@ -3888,53 +3823,6 @@ "url": "https://opencollective.com/popperjs" } }, - "node_modules/@rc-component/mini-decimal": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@rc-component/mini-decimal/-/mini-decimal-1.0.1.tgz", - "integrity": "sha512-9N8nRk0oKj1qJzANKl+n9eNSMUGsZtjwNuDCiZ/KA+dt1fE3zq5x2XxclRcAbOIXnZcJ53ozP2Pa60gyELXagA==", - "dependencies": { - "@babel/runtime": "^7.18.0" - }, - "engines": { - "node": ">=8.x" - } - }, - "node_modules/@rc-component/portal": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@rc-component/portal/-/portal-1.1.0.tgz", - "integrity": "sha512-tbXM9SB1r5FOuZjRCljERFByFiEUcMmCWMXLog/NmgCzlAzreXyf23Vei3ZpSMxSMavzPnhCovfZjZdmxS3d1w==", - "dependencies": { - "@babel/runtime": "^7.18.0", - "classnames": "^2.3.2", - "rc-util": "^5.24.4" - }, - "engines": { - "node": ">=8.x" - }, - "peerDependencies": { - "react": ">=16.9.0", - "react-dom": ">=16.9.0" - } - }, - "node_modules/@rc-component/tour": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@rc-component/tour/-/tour-1.1.0.tgz", - "integrity": "sha512-Cy45VnNEDq6DLF5eKonIflObDfofbPq7AJpSf18qLN+j9+wW+sNlRv3JnCMDUsCdhSlnM4+yJ1RMokKp9GCpOQ==", - "dependencies": { - "@babel/runtime": "^7.18.0", - "@rc-component/portal": "^1.0.0-9", - "classnames": "^2.3.2", - "rc-trigger": "^5.3.4", - "rc-util": "^5.24.4" - }, - "engines": { - "node": ">=8.x" - }, - "peerDependencies": { - "react": ">=16.9.0", - "react-dom": ">=16.9.0" - } - }, "node_modules/@react-aria/ssr": { "version": "3.4.1", "resolved": "https://registry.npmjs.org/@react-aria/ssr/-/ssr-3.4.1.tgz", @@ -4656,9 +4544,15 @@ "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==" }, "node_modules/@types/react": { - "version": "18.0.26", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.0.26.tgz", - "integrity": "sha512-hCR3PJQsAIXyxhTNSiDFY//LhnMZWpNNr5etoCqx/iUfGc5gXWtQR2Phl908jVR6uPXacojQWTg4qRpkxTuGug==", +<<<<<<< HEAD + "version": "17.0.52", + "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.52.tgz", + "integrity": "sha512-vwk8QqVODi0VaZZpDXQCmEmiOuyjEFPY7Ttaw5vjM112LOq37yz1CDJGrRJwA1fYEq4Iitd5rnjd1yWAc/bT+A==", +======= + "version": "17.0.53", + "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.53.tgz", + "integrity": "sha512-1yIpQR2zdYu1Z/dc1OxC+MA6GR240u3gcnP4l6mvj/PJiVaqHsQPmWttsvHsfnhfPbU2FuGmo0wSITPygjBmsw==", +>>>>>>> dev "dependencies": { "@types/prop-types": "*", "@types/scheduler": "*", @@ -5369,67 +5263,6 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/antd": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/antd/-/antd-5.1.2.tgz", - "integrity": "sha512-B6R6Bm0jIOb6v3JhS4DLKghJWqX0OufCOy99+R6F7QtGjbV/0wG/0rqH3DiQ4BHNIHsL+SDUYti9o3cT28lPww==", - "dependencies": { - "@ant-design/colors": "^6.0.0", - "@ant-design/cssinjs": "^1.3.0", - "@ant-design/icons": "^4.7.0", - "@ant-design/react-slick": "~1.0.0", - "@babel/runtime": "^7.18.3", - "@ctrl/tinycolor": "^3.4.0", - "@rc-component/tour": "~1.1.0", - "classnames": "^2.2.6", - "copy-to-clipboard": "^3.2.0", - "dayjs": "^1.11.1", - "qrcode.react": "^3.1.0", - "rc-cascader": "~3.8.0", - "rc-checkbox": "~2.3.0", - "rc-collapse": "~3.4.2", - "rc-dialog": "~9.0.2", - "rc-drawer": "~6.1.1", - "rc-dropdown": "~4.0.0", - "rc-field-form": "~1.27.0", - "rc-image": "~5.13.0", - "rc-input": "~0.1.4", - "rc-input-number": "~7.4.0", - "rc-mentions": "~1.13.1", - "rc-menu": "~9.8.0", - "rc-motion": "^2.6.1", - "rc-notification": "~5.0.0", - "rc-pagination": "~3.2.0", - "rc-picker": "~3.1.1", - "rc-progress": "~3.4.1", - "rc-rate": "~2.9.0", - "rc-resize-observer": "^1.2.0", - "rc-segmented": "~2.1.0", - "rc-select": "~14.2.0", - "rc-slider": "~10.0.0", - "rc-steps": "~6.0.0", - "rc-switch": "~4.0.0", - "rc-table": "~7.28.3", - "rc-tabs": "~12.5.1", - "rc-textarea": "~0.4.5", - "rc-tooltip": "~5.2.0", - "rc-tree": "~5.7.0", - "rc-tree-select": "~5.6.0", - "rc-trigger": "^5.2.10", - "rc-upload": "~4.3.0", - "rc-util": "^5.27.0", - "scroll-into-view-if-needed": "^3.0.3", - "throttle-debounce": "^5.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/ant-design" - }, - "peerDependencies": { - "react": ">=16.9.0", - "react-dom": ">=16.9.0" - } - }, "node_modules/anymatch": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", @@ -5486,11 +5319,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/array-tree-filter": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-tree-filter/-/array-tree-filter-2.1.0.tgz", - "integrity": "sha512-4ROwICNlNw/Hqa9v+rk5h22KjmzB1JGTMVKP2AKJBOCgb0yL0ASf0+YvCcLNNwquOHNX48jkeZIJ3a+oOQqKcw==" - }, "node_modules/array-union": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", @@ -5594,11 +5422,6 @@ "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==" }, - "node_modules/async-validator": { - "version": "4.2.5", - "resolved": "https://registry.npmjs.org/async-validator/-/async-validator-4.2.5.tgz", - "integrity": "sha512-7HhHjtERjqlNbZtqNqy2rckN/SpOOlmDliet+lP7k+eKZEjPk3DgyeU9lIXLdeLz0uBbbVp+9Qdow9wJWgwwfg==" - }, "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -6619,11 +6442,6 @@ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" }, - "node_modules/compute-scroll-into-view": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/compute-scroll-into-view/-/compute-scroll-into-view-2.0.4.tgz", - "integrity": "sha512-y/ZA3BGnxoM/QHHQ2Uy49CLtnWPbt4tTPpEEZiEmmiWBFKjej7nEyH8Ryz54jH0MLXflUYA3Er2zUxPSJu5R+g==" - }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -6679,14 +6497,6 @@ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" }, - "node_modules/copy-to-clipboard": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/copy-to-clipboard/-/copy-to-clipboard-3.3.3.tgz", - "integrity": "sha512-2KV8NhB5JqC3ky0r9PMCAZKbUHSwtEo4CwCs0KXgruG43gX5PMqDEBbVU4OUzw2MuAWUfsuFmWvEKG5QRfSnJA==", - "dependencies": { - "toggle-selection": "^1.0.6" - } - }, "node_modules/core-js": { "version": "3.27.1", "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.27.1.tgz", @@ -7205,11 +7015,6 @@ "node": ">=10" } }, - "node_modules/dayjs": { - "version": "1.11.7", - "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.7.tgz", - "integrity": "sha512-+Yw9U6YO5TQohxLcIkrXBeY73WP3ejHWVvx8XCk3gxvQDCTEmS48ZrSZCKciI7Bhl/uCMyxYtE9UqRILmFphkQ==" - }, "node_modules/debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", @@ -7481,11 +7286,6 @@ "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.15.tgz", "integrity": "sha512-8o+oVqLQZoruQPYy3uAAQtc6YbtSiRq5aPJBhJ82YTJRHvI6ofhYAkC81WmjFTnfUbqg6T3aCglIpU9p/5e7Cw==" }, - "node_modules/dom-align": { - "version": "1.12.4", - "resolved": "https://registry.npmjs.org/dom-align/-/dom-align-1.12.4.tgz", - "integrity": "sha512-R8LUSEay/68zE5c8/3BDxiTEvgb4xZTF0RKmAHfiEVN3klfIpXfi2/QCoiWPccVQ0J/ZGdz9OjzL4uJEP/MRAw==" - }, "node_modules/dom-converter": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.2.0.tgz", @@ -12323,14 +12123,6 @@ "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==" }, - "node_modules/json2mq": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/json2mq/-/json2mq-0.2.0.tgz", - "integrity": "sha512-SzoRg7ux5DWTII9J2qkrZrqV1gt+rTaoufMxEzXbS26Uid0NwaJd123HcoB80TgubEppxxIGdNxCx50fEoEWQA==", - "dependencies": { - "string-convert": "^0.2.0" - } - }, "node_modules/json5": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", @@ -14896,14 +14688,6 @@ "teleport": ">=0.2.0" } }, - "node_modules/qrcode.react": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/qrcode.react/-/qrcode.react-3.1.0.tgz", - "integrity": "sha512-oyF+Urr3oAMUG/OiOuONL3HXM+53wvuH3mtIWQrYmsXoAq0DkvZp2RYUWFSMFtbdOpuS++9v+WAkzNVkMlNW6Q==", - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0" - } - }, "node_modules/qs": { "version": "6.5.3", "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz", @@ -15004,611 +14788,6 @@ "node": ">=0.10.0" } }, - "node_modules/rc-align": { - "version": "4.0.15", - "resolved": "https://registry.npmjs.org/rc-align/-/rc-align-4.0.15.tgz", - "integrity": "sha512-wqJtVH60pka/nOX7/IspElA8gjPNQKIx/ZqJ6heATCkXpe1Zg4cPVrMD2vC96wjsFFL8WsmhPbx9tdMo1qqlIA==", - "dependencies": { - "@babel/runtime": "^7.10.1", - "classnames": "2.x", - "dom-align": "^1.7.0", - "rc-util": "^5.26.0", - "resize-observer-polyfill": "^1.5.1" - }, - "peerDependencies": { - "react": ">=16.9.0", - "react-dom": ">=16.9.0" - } - }, - "node_modules/rc-cascader": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/rc-cascader/-/rc-cascader-3.8.0.tgz", - "integrity": "sha512-zCz/NzsNRQ1TIfiR3rQNxjeRvgRHEkNdo0FjHQZ6Ay6n4tdCmMrM7+81ThNaf21JLQ1gS2AUG2t5uikGV78obA==", - "dependencies": { - "@babel/runtime": "^7.12.5", - "array-tree-filter": "^2.1.0", - "classnames": "^2.3.1", - "rc-select": "~14.2.0", - "rc-tree": "~5.7.0", - "rc-util": "^5.6.1" - }, - "peerDependencies": { - "react": ">=16.9.0", - "react-dom": ">=16.9.0" - } - }, - "node_modules/rc-checkbox": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/rc-checkbox/-/rc-checkbox-2.3.2.tgz", - "integrity": "sha512-afVi1FYiGv1U0JlpNH/UaEXdh6WUJjcWokj/nUN2TgG80bfG+MDdbfHKlLcNNba94mbjy2/SXJ1HDgrOkXGAjg==", - "dependencies": { - "@babel/runtime": "^7.10.1", - "classnames": "^2.2.1" - }, - "peerDependencies": { - "react": ">=16.9.0", - "react-dom": ">=16.9.0" - } - }, - "node_modules/rc-collapse": { - "version": "3.4.2", - "resolved": "https://registry.npmjs.org/rc-collapse/-/rc-collapse-3.4.2.tgz", - "integrity": "sha512-jpTwLgJzkhAgp2Wpi3xmbTbbYExg6fkptL67Uu5LCRVEj6wqmy0DHTjjeynsjOLsppHGHu41t1ELntZ0lEvS/Q==", - "dependencies": { - "@babel/runtime": "^7.10.1", - "classnames": "2.x", - "rc-motion": "^2.3.4", - "rc-util": "^5.2.1", - "shallowequal": "^1.1.0" - }, - "peerDependencies": { - "react": ">=16.9.0", - "react-dom": ">=16.9.0" - } - }, - "node_modules/rc-dialog": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/rc-dialog/-/rc-dialog-9.0.2.tgz", - "integrity": "sha512-s3U+24xWUuB6Bn2Lk/Qt6rufy+uT+QvWkiFhNBcO9APLxcFFczWamaq7x9h8SCuhfc1nHcW4y8NbMsnAjNnWyg==", - "dependencies": { - "@babel/runtime": "^7.10.1", - "@rc-component/portal": "^1.0.0-8", - "classnames": "^2.2.6", - "rc-motion": "^2.3.0", - "rc-util": "^5.21.0" - }, - "peerDependencies": { - "react": ">=16.9.0", - "react-dom": ">=16.9.0" - } - }, - "node_modules/rc-drawer": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/rc-drawer/-/rc-drawer-6.1.2.tgz", - "integrity": "sha512-mYsTVT8Amy0LRrpVEv7gI1hOjtfMSO/qHAaCDzFx9QBLnms3cAQLJkaxRWM+Eq99oyLhU/JkgoqTg13bc4ogOQ==", - "dependencies": { - "@babel/runtime": "^7.10.1", - "@rc-component/portal": "^1.0.0-6", - "classnames": "^2.2.6", - "rc-motion": "^2.6.1", - "rc-util": "^5.21.2" - }, - "peerDependencies": { - "react": ">=16.9.0", - "react-dom": ">=16.9.0" - } - }, - "node_modules/rc-dropdown": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/rc-dropdown/-/rc-dropdown-4.0.1.tgz", - "integrity": "sha512-OdpXuOcme1rm45cR0Jzgfl1otzmU4vuBVb+etXM8vcaULGokAKVpKlw8p6xzspG7jGd/XxShvq+N3VNEfk/l5g==", - "dependencies": { - "@babel/runtime": "^7.18.3", - "classnames": "^2.2.6", - "rc-trigger": "^5.3.1", - "rc-util": "^5.17.0" - }, - "peerDependencies": { - "react": ">=16.11.0", - "react-dom": ">=16.11.0" - } - }, - "node_modules/rc-field-form": { - "version": "1.27.3", - "resolved": "https://registry.npmjs.org/rc-field-form/-/rc-field-form-1.27.3.tgz", - "integrity": "sha512-HGqxHnmGQgkPApEcikV4qTg3BLPC82uB/cwBDftDt1pYaqitJfSl5TFTTUMKVEJVT5RqJ2Zi68ME1HmIMX2HAw==", - "dependencies": { - "@babel/runtime": "^7.18.0", - "async-validator": "^4.1.0", - "rc-util": "^5.8.0" - }, - "engines": { - "node": ">=8.x" - }, - "peerDependencies": { - "react": ">=16.9.0", - "react-dom": ">=16.9.0" - } - }, - "node_modules/rc-image": { - "version": "5.13.0", - "resolved": "https://registry.npmjs.org/rc-image/-/rc-image-5.13.0.tgz", - "integrity": "sha512-iZTOmw5eWo2+gcrJMMcnd7SsxVHl3w5xlyCgsULUdJhJbnuI8i/AL0tVOsE7aLn9VfOh1qgDT3mC2G75/c7mqg==", - "dependencies": { - "@babel/runtime": "^7.11.2", - "@rc-component/portal": "^1.0.2", - "classnames": "^2.2.6", - "rc-dialog": "~9.0.0", - "rc-motion": "^2.6.2", - "rc-util": "^5.0.6" - }, - "peerDependencies": { - "react": ">=16.9.0", - "react-dom": ">=16.9.0" - } - }, - "node_modules/rc-input": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/rc-input/-/rc-input-0.1.4.tgz", - "integrity": "sha512-FqDdNz+fV2dKNgfXzcSLKvC+jEs1709t7nD+WdfjrdSaOcefpgc7BUJYadc3usaING+b7ediMTfKxuJBsEFbXA==", - "dependencies": { - "@babel/runtime": "^7.11.1", - "classnames": "^2.2.1", - "rc-util": "^5.18.1" - }, - "peerDependencies": { - "react": ">=16.0.0", - "react-dom": ">=16.0.0" - } - }, - "node_modules/rc-input-number": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/rc-input-number/-/rc-input-number-7.4.0.tgz", - "integrity": "sha512-r/Oub/sPYbzqLNUOHnnc9sbCu78a81KX+RCbRwmpvB4W6nptUySbdWS5KHV4Hak5CAE1LAd+wWm5JjvZizG1FA==", - "dependencies": { - "@babel/runtime": "^7.10.1", - "@rc-component/mini-decimal": "^1.0.1", - "classnames": "^2.2.5", - "rc-util": "^5.23.0" - }, - "peerDependencies": { - "react": ">=16.9.0", - "react-dom": ">=16.9.0" - } - }, - "node_modules/rc-mentions": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/rc-mentions/-/rc-mentions-1.13.1.tgz", - "integrity": "sha512-FCkaWw6JQygtOz0+Vxz/M/NWqrWHB9LwqlY2RtcuFqWJNFK9njijOOzTSsBGANliGufVUzx/xuPHmZPBV0+Hgw==", - "dependencies": { - "@babel/runtime": "^7.10.1", - "classnames": "^2.2.6", - "rc-menu": "~9.8.0", - "rc-textarea": "^0.4.0", - "rc-trigger": "^5.0.4", - "rc-util": "^5.22.5" - }, - "peerDependencies": { - "react": ">=16.9.0", - "react-dom": ">=16.9.0" - } - }, - "node_modules/rc-menu": { - "version": "9.8.1", - "resolved": "https://registry.npmjs.org/rc-menu/-/rc-menu-9.8.1.tgz", - "integrity": "sha512-179weouypfjWJSRvvoo/vPy+StojsMzK2XC5jRNhL1ryt/N/8wAFESte8K6jZJkNp9DHDLFTe+dCGmikKpiFuA==", - "dependencies": { - "@babel/runtime": "^7.10.1", - "classnames": "2.x", - "rc-motion": "^2.4.3", - "rc-overflow": "^1.2.8", - "rc-trigger": "^5.1.2", - "rc-util": "^5.12.0", - "shallowequal": "^1.1.0" - }, - "peerDependencies": { - "react": ">=16.9.0", - "react-dom": ">=16.9.0" - } - }, - "node_modules/rc-motion": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/rc-motion/-/rc-motion-2.6.2.tgz", - "integrity": "sha512-4w1FaX3dtV749P8GwfS4fYnFG4Rb9pxvCYPc/b2fw1cmlHJWNNgOFIz7ysiD+eOrzJSvnLJWlNQQncpNMXwwpg==", - "dependencies": { - "@babel/runtime": "^7.11.1", - "classnames": "^2.2.1", - "rc-util": "^5.21.0" - }, - "peerDependencies": { - "react": ">=16.9.0", - "react-dom": ">=16.9.0" - } - }, - "node_modules/rc-notification": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/rc-notification/-/rc-notification-5.0.0.tgz", - "integrity": "sha512-mTqMD5Uip1tJhX74opHrmC/CG1wtkxuCcaiCaPDFWPJ/o50OgYYi3nPV10Pwgb8mK8dqg9DDQKISWAcKmcVlXQ==", - "dependencies": { - "@babel/runtime": "^7.10.1", - "classnames": "2.x", - "rc-motion": "^2.6.0", - "rc-util": "^5.20.1" - }, - "engines": { - "node": ">=8.x" - }, - "peerDependencies": { - "react": ">=16.9.0", - "react-dom": ">=16.9.0" - } - }, - "node_modules/rc-overflow": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/rc-overflow/-/rc-overflow-1.2.8.tgz", - "integrity": "sha512-QJ0UItckWPQ37ZL1dMEBAdY1dhfTXFL9k6oTTcyydVwoUNMnMqCGqnRNA98axSr/OeDKqR6DVFyi8eA5RQI/uQ==", - "dependencies": { - "@babel/runtime": "^7.11.1", - "classnames": "^2.2.1", - "rc-resize-observer": "^1.0.0", - "rc-util": "^5.19.2" - }, - "peerDependencies": { - "react": ">=16.9.0", - "react-dom": ">=16.9.0" - } - }, - "node_modules/rc-pagination": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/rc-pagination/-/rc-pagination-3.2.0.tgz", - "integrity": "sha512-5tIXjB670WwwcAJzAqp2J+cOBS9W3cH/WU1EiYwXljuZ4vtZXKlY2Idq8FZrnYBz8KhN3vwPo9CoV/SJS6SL1w==", - "dependencies": { - "@babel/runtime": "^7.10.1", - "classnames": "^2.2.1" - }, - "peerDependencies": { - "react": ">=16.9.0", - "react-dom": ">=16.9.0" - } - }, - "node_modules/rc-picker": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/rc-picker/-/rc-picker-3.1.4.tgz", - "integrity": "sha512-4qANXNc3C02YENNQvun329zf9VLvSQ2W8RkKQRu8k1P+EtSGqe3klcAKCfz/1TuCiDvgRjJlzRmyZAkwvsbI8w==", - "dependencies": { - "@babel/runtime": "^7.10.1", - "classnames": "^2.2.1", - "rc-trigger": "^5.0.4", - "rc-util": "^5.4.0", - "shallowequal": "^1.1.0" - }, - "engines": { - "node": ">=8.x" - }, - "peerDependencies": { - "date-fns": ">= 2.x", - "dayjs": ">= 1.x", - "moment": ">= 2.x", - "react": ">=16.9.0", - "react-dom": ">=16.9.0" - }, - "peerDependenciesMeta": { - "date-fns": { - "optional": true - }, - "dayjs": { - "optional": true - }, - "moment": { - "optional": true - } - } - }, - "node_modules/rc-progress": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/rc-progress/-/rc-progress-3.4.1.tgz", - "integrity": "sha512-eAFDHXlk8aWpoXl0llrenPMt9qKHQXphxcVsnKs0FHC6eCSk1ebJtyaVjJUzKe0233ogiLDeEFK1Uihz3s67hw==", - "dependencies": { - "@babel/runtime": "^7.10.1", - "classnames": "^2.2.6", - "rc-util": "^5.16.1" - }, - "peerDependencies": { - "react": ">=16.9.0", - "react-dom": ">=16.9.0" - } - }, - "node_modules/rc-rate": { - "version": "2.9.2", - "resolved": "https://registry.npmjs.org/rc-rate/-/rc-rate-2.9.2.tgz", - "integrity": "sha512-SaiZFyN8pe0Fgphv8t3+kidlej+cq/EALkAJAc3A0w0XcPaH2L1aggM8bhe1u6GAGuQNAoFvTLjw4qLPGRKV5g==", - "dependencies": { - "@babel/runtime": "^7.10.1", - "classnames": "^2.2.5", - "rc-util": "^5.0.1" - }, - "engines": { - "node": ">=8.x" - }, - "peerDependencies": { - "react": ">=16.9.0", - "react-dom": ">=16.9.0" - } - }, - "node_modules/rc-resize-observer": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/rc-resize-observer/-/rc-resize-observer-1.2.1.tgz", - "integrity": "sha512-g53PnWLeVOmt4XWkt2x+QlIdf/PhJSd7JqHhtMrUY370e7wJ+kxbgXicYqvENUcgFiiOiMCd07YsC2GNsoSbnA==", - "dependencies": { - "@babel/runtime": "^7.20.7", - "classnames": "^2.2.1", - "rc-util": "^5.27.0", - "resize-observer-polyfill": "^1.5.1" - }, - "peerDependencies": { - "react": ">=16.9.0", - "react-dom": ">=16.9.0" - } - }, - "node_modules/rc-segmented": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/rc-segmented/-/rc-segmented-2.1.0.tgz", - "integrity": "sha512-hUlonro+pYoZcwrH6Vm56B2ftLfQh046hrwif/VwLIw1j3zGt52p5mREBwmeVzXnSwgnagpOpfafspzs1asjGw==", - "dependencies": { - "@babel/runtime": "^7.11.1", - "classnames": "^2.2.1", - "rc-motion": "^2.4.4", - "rc-util": "^5.17.0" - }, - "peerDependencies": { - "react": ">=16.0.0", - "react-dom": ">=16.0.0" - } - }, - "node_modules/rc-select": { - "version": "14.2.0", - "resolved": "https://registry.npmjs.org/rc-select/-/rc-select-14.2.0.tgz", - "integrity": "sha512-tvxHmbAA0EIhBkB7dyaRhcBUIWHocQbUFY/fBlezj2jg5p65a5VQ/UhBg2I9TA1wjpsr5CCx0ruZPkYcUMjDoQ==", - "dependencies": { - "@babel/runtime": "^7.10.1", - "classnames": "2.x", - "rc-motion": "^2.0.1", - "rc-overflow": "^1.0.0", - "rc-trigger": "^5.0.4", - "rc-util": "^5.16.1", - "rc-virtual-list": "^3.4.13" - }, - "engines": { - "node": ">=8.x" - }, - "peerDependencies": { - "react": "*", - "react-dom": "*" - } - }, - "node_modules/rc-slider": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/rc-slider/-/rc-slider-10.0.1.tgz", - "integrity": "sha512-igTKF3zBet7oS/3yNiIlmU8KnZ45npmrmHlUUio8PNbIhzMcsh+oE/r2UD42Y6YD2D/s+kzCQkzQrPD6RY435Q==", - "dependencies": { - "@babel/runtime": "^7.10.1", - "classnames": "^2.2.5", - "rc-util": "^5.18.1", - "shallowequal": "^1.1.0" - }, - "engines": { - "node": ">=8.x" - }, - "peerDependencies": { - "react": ">=16.9.0", - "react-dom": ">=16.9.0" - } - }, - "node_modules/rc-steps": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/rc-steps/-/rc-steps-6.0.0.tgz", - "integrity": "sha512-+KfMZIty40mYCQSDvYbZ1jwnuObLauTiIskT1hL4FFOBHP6ZOr8LK0m143yD3kEN5XKHSEX1DIwCj3AYZpoeNQ==", - "dependencies": { - "@babel/runtime": "^7.16.7", - "classnames": "^2.2.3", - "rc-util": "^5.16.1" - }, - "engines": { - "node": ">=8.x" - }, - "peerDependencies": { - "react": ">=16.9.0", - "react-dom": ">=16.9.0" - } - }, - "node_modules/rc-switch": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/rc-switch/-/rc-switch-4.0.0.tgz", - "integrity": "sha512-IfrYC99vN0gKaTyjQdqYuADU0eH00SAFHg3jOp8HrmUpJruhV1SohJzrCbPqPraZeX/6X/QKkdLfkdnUub05WA==", - "dependencies": { - "@babel/runtime": "^7.10.1", - "classnames": "^2.2.1", - "rc-util": "^5.0.1" - }, - "peerDependencies": { - "react": ">=16.9.0", - "react-dom": ">=16.9.0" - } - }, - "node_modules/rc-table": { - "version": "7.28.3", - "resolved": "https://registry.npmjs.org/rc-table/-/rc-table-7.28.3.tgz", - "integrity": "sha512-jiPtBDqcs0wF0KOJgkhDgxN6+vq4jHbteddE15IR6RajlVkAk+kRIecyBY28b+vg199yQiu/NGuSRKJKEGOWBQ==", - "dependencies": { - "@babel/runtime": "^7.10.1", - "classnames": "^2.2.5", - "rc-resize-observer": "^1.1.0", - "rc-util": "^5.22.5", - "shallowequal": "^1.1.0" - }, - "engines": { - "node": ">=8.x" - }, - "peerDependencies": { - "react": ">=16.9.0", - "react-dom": ">=16.9.0" - } - }, - "node_modules/rc-tabs": { - "version": "12.5.6", - "resolved": "https://registry.npmjs.org/rc-tabs/-/rc-tabs-12.5.6.tgz", - "integrity": "sha512-aArXHzxK7YICxe+622CZ8FlO5coMi8P7E6tXpseCPKm1gdTjUt0LrQK1/AxcrRXZXG3K4QqhlKmET0+cX5DQaQ==", - "dependencies": { - "@babel/runtime": "^7.11.2", - "classnames": "2.x", - "rc-dropdown": "~4.0.0", - "rc-menu": "~9.8.0", - "rc-motion": "^2.6.2", - "rc-resize-observer": "^1.0.0", - "rc-util": "^5.16.0" - }, - "engines": { - "node": ">=8.x" - }, - "peerDependencies": { - "react": ">=16.9.0", - "react-dom": ">=16.9.0" - } - }, - "node_modules/rc-textarea": { - "version": "0.4.7", - "resolved": "https://registry.npmjs.org/rc-textarea/-/rc-textarea-0.4.7.tgz", - "integrity": "sha512-IQPd1CDI3mnMlkFyzt2O4gQ2lxUsnBAeJEoZGJnkkXgORNqyM9qovdrCj9NzcRfpHgLdzaEbU3AmobNFGUznwQ==", - "dependencies": { - "@babel/runtime": "^7.10.1", - "classnames": "^2.2.1", - "rc-resize-observer": "^1.0.0", - "rc-util": "^5.24.4", - "shallowequal": "^1.1.0" - }, - "peerDependencies": { - "react": ">=16.9.0", - "react-dom": ">=16.9.0" - } - }, - "node_modules/rc-tooltip": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/rc-tooltip/-/rc-tooltip-5.2.2.tgz", - "integrity": "sha512-jtQzU/18S6EI3lhSGoDYhPqNpWajMtS5VV/ld1LwyfrDByQpYmw/LW6U7oFXXLukjfDHQ7Ju705A82PRNFWYhg==", - "dependencies": { - "@babel/runtime": "^7.11.2", - "classnames": "^2.3.1", - "rc-trigger": "^5.0.0" - }, - "peerDependencies": { - "react": ">=16.9.0", - "react-dom": ">=16.9.0" - } - }, - "node_modules/rc-tree": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/rc-tree/-/rc-tree-5.7.2.tgz", - "integrity": "sha512-nmnL6qLnfwVckO5zoqKL2I9UhwDqzyCtjITQCkwhimyz1zfuFkG5ZPIXpzD/Guzso94qQA/QrMsvzic5W6QDjg==", - "dependencies": { - "@babel/runtime": "^7.10.1", - "classnames": "2.x", - "rc-motion": "^2.0.1", - "rc-util": "^5.16.1", - "rc-virtual-list": "^3.4.8" - }, - "engines": { - "node": ">=10.x" - }, - "peerDependencies": { - "react": "*", - "react-dom": "*" - } - }, - "node_modules/rc-tree-select": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/rc-tree-select/-/rc-tree-select-5.6.0.tgz", - "integrity": "sha512-XG6pu0a9l6+mzhQqUYfR2VIONbe/3LjVc3wKt28k6uBMZsI1j+SSxRyt/7jWRq8Kok8jHJBQASlDg6ehr9Sp0w==", - "dependencies": { - "@babel/runtime": "^7.10.1", - "classnames": "2.x", - "rc-select": "~14.2.0", - "rc-tree": "~5.7.0", - "rc-util": "^5.16.1" - }, - "peerDependencies": { - "react": "*", - "react-dom": "*" - } - }, - "node_modules/rc-trigger": { - "version": "5.3.4", - "resolved": "https://registry.npmjs.org/rc-trigger/-/rc-trigger-5.3.4.tgz", - "integrity": "sha512-mQv+vas0TwKcjAO2izNPkqR4j86OemLRmvL2nOzdP9OWNWA1ivoTt5hzFqYNW9zACwmTezRiN8bttrC7cZzYSw==", - "dependencies": { - "@babel/runtime": "^7.18.3", - "classnames": "^2.2.6", - "rc-align": "^4.0.0", - "rc-motion": "^2.0.0", - "rc-util": "^5.19.2" - }, - "engines": { - "node": ">=8.x" - }, - "peerDependencies": { - "react": ">=16.9.0", - "react-dom": ">=16.9.0" - } - }, - "node_modules/rc-upload": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/rc-upload/-/rc-upload-4.3.4.tgz", - "integrity": "sha512-uVbtHFGNjHG/RyAfm9fluXB6pvArAGyAx8z7XzXXyorEgVIWj6mOlriuDm0XowDHYz4ycNK0nE0oP3cbFnzxiQ==", - "dependencies": { - "@babel/runtime": "^7.18.3", - "classnames": "^2.2.5", - "rc-util": "^5.2.0" - }, - "peerDependencies": { - "react": ">=16.9.0", - "react-dom": ">=16.9.0" - } - }, - "node_modules/rc-util": { - "version": "5.27.1", - "resolved": "https://registry.npmjs.org/rc-util/-/rc-util-5.27.1.tgz", - "integrity": "sha512-PsjHA+f+KBCz+YTZxrl3ukJU5RoNKoe3KSNMh0xGiISbR67NaM9E9BiMjCwxa3AcCUOg/rZ+V0ZKLSimAA+e3w==", - "dependencies": { - "@babel/runtime": "^7.18.3", - "react-is": "^16.12.0" - }, - "peerDependencies": { - "react": ">=16.9.0", - "react-dom": ">=16.9.0" - } - }, - "node_modules/rc-util/node_modules/react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" - }, - "node_modules/rc-virtual-list": { - "version": "3.4.13", - "resolved": "https://registry.npmjs.org/rc-virtual-list/-/rc-virtual-list-3.4.13.tgz", - "integrity": "sha512-cPOVDmcNM7rH6ANotanMDilW/55XnFPw0Jh/GQYtrzZSy3AmWvCnqVNyNC/pgg3lfVmX2994dlzAhuUrd4jG7w==", - "dependencies": { - "@babel/runtime": "^7.20.0", - "classnames": "^2.2.6", - "rc-resize-observer": "^1.0.0", - "rc-util": "^5.15.0" - }, - "engines": { - "node": ">=8.x" - }, - "peerDependencies": { - "react": "*", - "react-dom": "*" - } - }, "node_modules/react": { "version": "18.2.0", "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", @@ -15729,6 +14908,14 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" }, + "node_modules/react-kakao-login": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/react-kakao-login/-/react-kakao-login-2.1.1.tgz", + "integrity": "sha512-t9htk41/i0zUY7q92mtqdqVZZ018BPi1DgbSVVrPCmuMKhZGJOnZ9OfaKLVPu3sn8QXbJc3dPwqKOiElpb44hQ==", + "peerDependencies": { + "react": ">= 15.3.0" + } + }, "node_modules/react-lifecycles-compat": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", @@ -16112,11 +15299,6 @@ "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==" }, - "node_modules/resize-observer-polyfill": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz", - "integrity": "sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==" - }, "node_modules/resolve": { "version": "1.22.1", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", @@ -16439,14 +15621,6 @@ "url": "https://opencollective.com/webpack" } }, - "node_modules/scroll-into-view-if-needed": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/scroll-into-view-if-needed/-/scroll-into-view-if-needed-3.0.4.tgz", - "integrity": "sha512-s+/F50jwTOUt+u5oEIAzum9MN2lUQNvWBe/zfEsVQcbaERjGkKLq1s+2wCHkahMLC8nMLbzMVKivx9JhunXaZg==", - "dependencies": { - "compute-scroll-into-view": "^2.0.4" - } - }, "node_modules/select-hose": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", @@ -16879,11 +16053,6 @@ "safe-buffer": "~5.2.0" } }, - "node_modules/string-convert": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/string-convert/-/string-convert-0.2.1.tgz", - "integrity": "sha512-u/1tdPl4yQnPBjnVrmdLo9gtuLvELKsAoRapekWggdiQNvvvum+jYF329d84NAa660KQw7pB2n36KrIKVoXa3A==" - }, "node_modules/string-length": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", @@ -17482,14 +16651,6 @@ "resolved": "https://registry.npmjs.org/throat/-/throat-6.0.2.tgz", "integrity": "sha512-WKexMoJj3vEuK0yFEapj8y64V0A6xcuPuK9Gt1d0R+dzCSJc0lHqQytAbSB4cDAK0dWh4T0E2ETkoLE2WZ41OQ==" }, - "node_modules/throttle-debounce": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/throttle-debounce/-/throttle-debounce-5.0.0.tgz", - "integrity": "sha512-2iQTSgkkc1Zyk0MeVrt/3BvuOXYPl/R8Z0U2xxo9rjwNciaHDG3R+Lm6dh4EeUci49DanvBnuqI6jshoQQRGEg==", - "engines": { - "node": ">=12.22" - } - }, "node_modules/thunky": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", @@ -17524,11 +16685,6 @@ "node": ">=8.0" } }, - "node_modules/toggle-selection": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/toggle-selection/-/toggle-selection-1.0.6.tgz", - "integrity": "sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ==" - }, "node_modules/toidentifier": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", @@ -17708,6 +16864,19 @@ "is-typedarray": "^1.0.0" } }, + "node_modules/typescript": { + "version": "4.9.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.4.tgz", + "integrity": "sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg==", + "peer": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, "node_modules/unbox-primitive": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", diff --git a/FE/package.json b/FE/package.json index 24ec59d..192be5b 100644 --- a/FE/package.json +++ b/FE/package.json @@ -6,18 +6,19 @@ "@emotion/react": "^11.10.5", "@emotion/styled": "^11.10.5", "@material-ui/core": "^4.12.4", + "@material-ui/icons": "^4.11.3", "@mui/icons-material": "^5.11.0", - "@mui/material": "^5.11.3", + "@mui/material": "^5.11.4", "@testing-library/jest-dom": "^5.16.5", "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^13.5.0", - "antd": "^5.0.7", "axios": "^1.2.1", "bootstrap": "^5.2.3", "dependencies": "^0.0.1", "react": "^18.2.0", "react-bootstrap": "^2.7.0", "react-dom": "^18.2.0", + "react-kakao-login": "^2.1.1", "react-router-dom": "^6.4.5", "react-scripts": "5.0.1", "styled-components": "^5.3.6", @@ -46,5 +47,6 @@ "last 1 firefox version", "last 1 safari version" ] - } + }, + "proxy" :"http://localhost:8080" } diff --git a/FE/src/App.css b/FE/src/App.css index 0b96be3..0134a93 100644 --- a/FE/src/App.css +++ b/FE/src/App.css @@ -51,4 +51,9 @@ @font-face { font-family: "NotoSansKR-300"; src: url("./fonts/NotoSansKR-Light.otf"); +} + +@font-face { + font-family: "NotoSansKR-500"; + src: url("./fonts/NotoSansKR-Medium.otf"); } \ No newline at end of file diff --git a/FE/src/containers/findinfo/EmailFindDe.jsx b/FE/src/containers/findinfo/EmailFindDe.jsx new file mode 100644 index 0000000..260d849 --- /dev/null +++ b/FE/src/containers/findinfo/EmailFindDe.jsx @@ -0,0 +1,109 @@ +import React from "react"; +import { useNavigate } from 'react-router-dom'; +import styled from "styled-components"; + +function EmailFindDe() { + const Style={ + Wrapper:styled.div` + box-sizing: border-box; + position: absolute; + width: 906px; + height: 234px; + top: 248px; + background: #FFFFFF; + border: 1px solid #D0D0D0; + box-shadow: 0px 4px 4px rgba(0, 0, 0, 0.25); + border-radius: 30px; + `, + Title:styled.div` + position: absolute; + width: 664px; + height: 64px; + top: 154px; + font-family: 'Noto Sans KR'; + font-style: normal; + font-weight: 700; + font-size: 40px; + line-height: 58px; + text-align: center; + color: #464646; + `, + FormStyle:styled.div` + display: flex; + justify-content:center; + `, + Question:styled.div` + display: flex; + justify-content: center; + width:633px; + height:43px; + position: relative; + margin-bottom: 22px; + left:145px; + top:63px; + `, + Answer:styled.input` + position: absolute; + width: 547px; + height: 43px; + background: #FFFFFF; + border: 1px solid #B0B0B0; + box-shadow: 0px 4px 4px rgba(0, 0, 0, 0.25); + border-radius: 10px; + display: flex; + justify-content: right; + right:0px; + padding-left: 20px; + ::placeholder { + font-family: 'Inter'; + font-style: normal; + font-weight: 400; + font-size: 15px; + line-height: 18px; + text-align: center; + color: #B0B0B0; + } + `, + InputName:styled.div` + position: absolute; + font-family: 'Noto Sans KR'; + font-size: 20px; + line-height: 29px; + width: 60px; + height: 32px; + left: 0px; + text-align: right; + `, + EmailButton:styled.button` + box-sizing: border-box; + position: absolute; + width: 906px; + height: 68px; + top: 512px; + background: #FADA5E; + border: 1px solid #D0D0D0; + box-shadow: 0px 4px 4px rgba(0, 0, 0, 0.25); + border-radius: 30px; + color:white; + `, + } + return ( + <> + 이메일 찾기 + + + + 이름 + + + + 연락처 + + + + 이메일 찾기 + + + ) +} +export default EmailFindDe; \ No newline at end of file diff --git a/FE/src/containers/findinfo/PasswordFindDe.jsx b/FE/src/containers/findinfo/PasswordFindDe.jsx new file mode 100644 index 0000000..535c716 --- /dev/null +++ b/FE/src/containers/findinfo/PasswordFindDe.jsx @@ -0,0 +1,112 @@ +import React from "react"; +import styled from "styled-components"; + +function PasswordFindDe() { + const Style={ + Wrapper:styled.div` + box-sizing: border-box; + position: absolute; + width: 906px; + height: 381px; + top: 248px; + background: #FFFFFF; + border: 1px solid #D0D0D0; + box-shadow: 0px 4px 4px rgba(0, 0, 0, 0.25); + border-radius: 30px; + `, + Title:styled.div` + position: absolute; + width: 664px; + height: 64px; + top: 154px; + font-family: 'Noto Sans KR'; + font-style: normal; + font-weight: 700; + font-size: 40px; + line-height: 58px; + text-align: center; + color: #464646; + `, + FormStyle:styled.div` + display: flex; + justify-content:center; + `, + Question:styled.div` + display: flex; + justify-content: center; + width:633px; + height:43px; + position: relative; + margin-bottom: 22px; + left:145px; + top:95px; + `, + Answer:styled.input` + position: absolute; + width: 547px; + height: 43px; + background: #FFFFFF; + border: 1px solid #B0B0B0; + box-shadow: 0px 4px 4px rgba(0, 0, 0, 0.25); + border-radius: 10px; + display: flex; + justify-content: right; + right:0px; + padding-left: 20px; + ::placeholder { + font-family: 'Inter'; + font-style: normal; + font-weight: 400; + font-size: 15px; + line-height: 18px; + text-align: center; + color: #B0B0B0; + } + `, + InputName:styled.div` + position: absolute; + font-family: 'Noto Sans KR'; + font-size: 20px; + line-height: 29px; + width: 60px; + height: 32px; + left: 0px; + text-align: right; + `, + PasswordButton:styled.button` + box-sizing: border-box; + position: absolute; + width: 906px; + height: 68px; + top: 657px; + background: #FADA5E; + border: 1px solid #D0D0D0; + box-shadow: 0px 4px 4px rgba(0, 0, 0, 0.25); + border-radius: 30px; + color:white; + `, + } + return ( + <> + 비밀번호 찾기 + + + + 이메일 + + + + 이름 + + + + 연락처 + + + + 비밀번호 찾기 + + + ) +} +export default PasswordFindDe; \ No newline at end of file diff --git a/FE/src/containers/login/Login.module.css b/FE/src/containers/login/Login.module.css index feaa52d..119b67d 100644 --- a/FE/src/containers/login/Login.module.css +++ b/FE/src/containers/login/Login.module.css @@ -2,7 +2,7 @@ width: 300px; height: 43px; font-size: 20px; - border: 1px solid #000000; + border: 1px solid #B0B0B0; outline: none; margin: 5px; background: #FFFFFF; @@ -12,27 +12,27 @@ input::-webkit-input-placeholder { color: #B0B0B0; - font-size: 15px; + font-size: 0.85rem; } input:-ms-input-placeholder { - color: #B0B0B0;font-size: 15px; + color: #B0B0B0;font-size: 0.85rem; } textarea::-webkit-input-placeholder { - color: #B0B0B0;font-size: 15px; + color: #B0B0B0;font-size: 0.85rem; } textarea:-ms-input-placeholder { - color: #B0B0B0;font-size: 15px; + color: #B0B0B0;font-size: 0.85rem; } .button{ - width: 300px; + width: 42%; height: 45px; - background: #FFF48C; + background: #FADA5E; border-radius: 10px; - border:#FFF48C; - font-family: "NotoSansKR-400"; + border:#FADA5E; + font-family: "NotoSansKR-700"; font-size: 15px; text-align: center; cursor: pointer; -} \ No newline at end of file +} diff --git a/FE/src/containers/login/LoginContainer.jsx b/FE/src/containers/login/LoginContainer.jsx index b302aa9..4133dca 100644 --- a/FE/src/containers/login/LoginContainer.jsx +++ b/FE/src/containers/login/LoginContainer.jsx @@ -1,102 +1,153 @@ import React, { useState, useEffect } from "react"; import axios from "axios"; import styles from "./Login.module.css"; -import kakao_login from "../../images/kakako_login.png"; +import kakao_login_large from "../../images/kakao_login_large.png"; +import kakao_login_small from "../../images/kakao_login_small.png"; +import { KAKAO_AUTH_URL } from "./kakaodata"; import { useNavigate } from "react-router-dom"; -import "bootstrap/dist/css/bootstrap.min.css"; -import Form from "react-bootstrap/Form"; -import Row from "react-bootstrap/Row"; -import Col from "react-bootstrap/Col"; -import Container from "react-bootstrap/Container"; - function LoginContainer() { - const KAKAO_AUTH_URL = "https://developers.kakao.com/tool/resource/login"; - const [inputEmail, setInputEmail] = useState(""); const [inputPw, setInputPw] = useState(""); - const [inputEmailError, setInputEmailError] = useState(""); + const [inputEmailError, setInputEmailError] = useState(false); + const [inputPwError, setInputPwError] = useState(false); + const [errorMessage, setErrorMessage] = useState(""); const navigate = useNavigate(); + const [windowSize, setWindowSize] = useState({ + width: undefined, + height: undefined, + }); + useEffect(() => { + function handleResize() { + setWindowSize({ + width: window.innerWidth, + heigth: window.innerHeight, + }); + } + window.addEventListener("resize", handleResize); + handleResize(); + return () => window.removeEventListener("resize", handleResize); + }, []); + const onChangeEmail = (e) => { + setInputEmailError(false); + // 유효성 검사 const emailRegex = /^(([^<>()\[\].,;:\s@"]+(\.[^<>()\[\].,;:\s@"]+)*)|(".+"))@(([^<>()[\].,;:\s@"]+\.)+[^<>()[\].,;:\s@"]{2,})$/i; - if (!e.target.value || emailRegex.test(e.target.value)) + if (emailRegex.test(e.target.value)) { setInputEmailError(false); // 정상 - else setInputEmailError(true); + console.log("email correct"); + } else { + setInputEmailError(true); + console.log("email error"); + setErrorMessage("이메일 형식이 맞지 않습니다."); + } + setInputEmail(e.target.value); }; const onChangePw = (e) => { + setInputPwError(false); + if (e.target.value.length < 8 || e.target.value.length > 20) { + console.log("pw error"); + setInputPwError(true); + setErrorMessage("비밀번호는 8자 이상 20자 이하입니다."); + } else { + console.log("pw correct"); + setInputPwError(false); + } setInputPw(e.target.value); }; const validation = () => { if (inputEmailError) { - alert("이메일 형식이 맞지 않습니다."); + // email 유효성 검사 탈락 + alert(errorMessage); return false; } - // 백엔드에서 검사 - /*if (inputPw.length < 4 || inputPw.length > 16) { - alert("비밀번호는 4자 이상 16자 이하입니다."); + // 백엔드에서도 검사 + // 프론트에서도 검사 + if (inputPwError) { + // pw 유효성 검사(길이) 탈락 + alert(errorMessage); return false; - }*/ + } return true; }; + const kakaoLogin = () => { + const code = new URL(window.location.href).searchParams.get("code"); + + localStorage.setItem("code", code); + + const getToken = () => { + axios({ + method: "GET", + url: "http://", + }); + }; + }; + const onClickLogin = () => { if (validation()) { + // 유효성 검사를 통과했다면 + // alert("통과"); const LoginForm = { email: inputEmail, password: inputPw, }; const config = { "Conteny-Type": "application/json" }; axios - .post("/login", LoginForm, config) + .post("http://localhost:8080/login", LoginForm, config) .then((result) => { console.log(result); - if (result.status === 200 && result.data !== "login fail") { - localStorage.setItem("username", result.data); - alert("로그인 완료"); - navigate("/"); + if (result.status === 200) { + if (result.data === "login success") { + //localStorage.setItem("username", result.data); + alert("로그인 완료"); + navigate("/"); + } else if (result.data === "login fail") { + // 회원 정보 불일치 + alert("정보가 일치하지 않습니다."); + } } else { - // 회원 정보 불일치 - alert("정보가 일치하지 않습니다."); + // status가 200이 아닌 경우 } // 작업 완료 되면 페이지 이동(새로고침) }) .catch((error) => { + console.log("다시 시도하세요"); alert(error.response.data.detail); }); } }; - const onClickID = () => { - // ID 찾기 페이지로 이동 + const onClickEM = () => { + navigate("/emailfind"); }; + const onClickPW = () => { - // PW 찾기 페이지로 이동 + navigate("/passwordfind"); }; const onClickRegister = () => { // 회원가입 페이지로 이동 + navigate("/signup"); }; return (
@@ -107,7 +158,6 @@ function LoginContainer() {
더 많은 기능을 이용해보세요!

- {/* ID */}
+ {/* ID */} = 840 ? { width: "300px" } : { width: "150px" } + } /> - +
{/* PASSWORD */} = 840 ? { width: "300px" } : { width: "150px" } + } />
@@ -146,15 +200,48 @@ function LoginContainer() { paddingBottom: "10px", }} > - + {windowSize.width >= 840 ? ( + + ) : ( + + )}
{/* 카카오로 로그인 버튼*/} - - kakaologin - + {windowSize.width >= 840 ? ( + + kakaologin + + ) : ( + + kakaologin + + )} + {/* 회원가입, 비밀번호 찾기 */}
@@ -175,7 +262,7 @@ function LoginContainer() {
- 아이디나 비밀번호가 기억나지 않나요?   | + 이메일, 비밀번호가 기억나지 않나요?   | - 아이디 찾기 + 이메일 찾기 | (props.state ? "#FADA5E" : "#C8D1E0")}; + color: ${(props) => (props.buttonColor ? "#464646" : "white")}; + border-radius: 10px; + border: #fff48c; + font-family: "NotoSansKR-700"; + font-size: 15px; + text-align: center; + cursor: ${(props) => (props.state ? "pointer" : "default")}; + box-shadow: none; + + justify-content: center; + align-items: center; + display: flex; +`; + +function SignUpContainer() { + /* 변수 */ + const [uemail, setUemail] = useState(""); + const [upw, setUpw] = useState(""); + const [confirmupw, setConfirmupw] = useState(""); + const [uname, setUname] = useState(""); + const [ubirth, setUbirth] = useState(""); + const [usex, setUsex] = useState(true); + const [utel, setUtel] = useState(""); + const [uconsent, setUconsent] = useState(false); // 선택 동의란 + const [unickname, setUnickname] = useState(""); + + /* 약관 동의 체크박스 */ + const [checkList, setCheckList] = useState([]); + const [buttonColor, setButtonColor] = useState(false); + const checkAll = (e) => { + e.target.checked + ? setCheckList(["terms", "collect", "marketing"]) + : setCheckList([]); + }; + const check = (e) => { + e.target.checked + ? setCheckList([...checkList, e.target.name]) + : setCheckList(checkList.filter((choice) => choice !== e.target.name)); + }; + + /* 에러 판별 변수 */ + const [uemailError, setUemailError] = useState(true); + const [upwLetterError, setUpwLetterError] = useState(true); + const [upwLengthError, setUpwLengthError] = useState(true); + const [confirmupwError, setConfirmupwError] = useState(true); + const [utelError, setUtelError] = useState(true); + const [ubirthError, setUbirthError] = useState(true); + const [unameError, setUnameError] = useState(true); + const [unicknameError, setUnicknameError] = useState(true); + + /* 오류 메세지 상태 저장 */ + const [emailMessage, setEmailMessage] = useState(""); + const [pwLetterMessage, setPwLetterMessage] = useState(""); + const [pwLengthMessage, setPwLengthMessage] = useState(""); + const [nameMessage, setNameMessage] = useState(""); + const [confirmpwMessage, setConfirmpwMessage] = useState(""); + const [telMessage, setTelMessage] = useState(""); + const [birthMessage, setBirthMessage] = useState(""); + const [nicknameMessage, setNicknameMessage] = useState(""); + + /* 날짜 계산 */ + const [form, setForm] = useState({ + year: "2000", + month: "01", + day: "01", + }); + function calcDate() { + const now = new Date(); + let years = []; + for (let y = now.getFullYear(); y >= 1930; y -= 1) { + years.push(y); + } + let month = []; + for (let m = 1; m <= 12; m += 1) { + if (m < 10) { + // 날짜가 2자리로 나타나야 했기 때문에 1자리 월에 0을 붙혀준다 + month.push("0" + m.toString()); + } else { + month.push(m.toString()); + } + } + let days = []; + let date = new Date(form.year, form.month, 0).getDate(); + // 오늘 이후의 날짜는 입력받으면 안되므로 date 수정 + if (form.year == now.getFullYear() && form.month == now.getMonth() + 1) { + date = now.getDate(); + } + for (let d = 1; d <= date; d += 1) { + if (d < 10) { + // 날짜가 2자리로 나타나야 했기 때문에 1자리 일에 0을 붙혀준다 + days.push("0" + d.toString()); + } else { + days.push(d.toString()); + } + } + + return { years, month, days }; + } + var d = calcDate(); + + /* 이메일 */ + const onChangeEmail = useCallback((e) => { + console.log("e.target.value : " + e.target.value); + setUemail(e.target.value); + // uemail로 검사를 하면 비동기라서 반응이 느림. e.target.value로 진행 + /* 이메일 유효성 검사 */ + const emailRegex = + /([\w-.]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([\w-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$/; + console.log("uemail : " + uemail); + if (emailRegex.test(e.target.value)) { + setUemailError(false); + setEmailMessage("올바른 형식입니다."); + console.log(uemailError); + console.log(emailMessage); + } else { + setUemailError(true); + setEmailMessage("이메일이 유효하지 않습니다."); + console.log(uemailError); + console.log(emailMessage); + } + }, []); + + /* 비밀번호 */ + const onChangePw = useCallback((e) => { + setUpw(e.target.value); + + /* 비밀번호 유효성 검사 */ + const passwordRegex = /^(?=.*?[A-z])(?=.*?[0-9]).{1,}$/; + if (passwordRegex.test(e.target.value)) { + setUpwLetterError(false); + } else { + setUpwLetterError(true); // 오류 + setPwLetterMessage("비밀번호는 대소문자와 숫자를 포함해야 합니다."); + } + if (e.target.value.length < 8 || e.target.value.length > 20) { + setUpwLengthError(true); // 오류 + setPwLengthMessage("비밀번호는 8자 이상, 20자 이하여야 합니다."); + } else { + setUpwLengthError(false); + } + }, []); + + /* 확인 비밀번호 */ + const onChangeConfirmupw = useCallback( + (e) => { + setConfirmupw(e.target.value); + + /* 확인 비밀번호 일치 검사 */ + if (upw === e.target.value) { + setConfirmupwError(false); + } else { + setConfirmupwError(true); + setConfirmpwMessage("입력하신 비밀번호와 일치하지 않습니다."); + } + }, + [upw] + ); + + /* 이름 */ + const onChangeName = useCallback((e) => { + setUname(e.target.value); + if (e.target.value.length < 2) { + setUnameError(true); + setNameMessage("이름을 입력해주세요."); + } else { + setUnameError(false); + setNameMessage(""); + } + }, []); + + /* 생년월일 YYYYMMDD */ + useEffect(() => { + var b = form.year + form.month + form.day; + setUbirth(b); + if (isChild(b)) { + setUbirthError(true); + setBirthMessage("만 14세 미만은 가입할 수 없습니다."); + } else { + setUbirthError(false); + } + }, [form]); + function isChild(birthDate) { + var today = new Date(); + var yyyy = today.getFullYear(); + var mm = + today.getMonth() < 9 + ? "0" + (today.getMonth() + 1) + : today.getMonth() + 1; + var dd = today.getDate() < 10 ? "0" + today.getDate() : today.getDate(); + + // true :만 14세 미만 + return parseInt(yyyy + mm + dd) - parseInt(birthDate) - 140000 < 0; + } + + /* 성별 */ + const onChangeSex = useCallback((e) => { + setUsex(true); + if (e.target.value === "male") { + // 남자는 true + setUsex(true); + } else { + // 여자는 false + setUsex(false); + } + }, []); + + /* 연락처 */ + const onChangeTel = useCallback((e) => { + setUtel(e.target.value); + + /* 연락처 유효성 검사 */ + if (isTel(e.target.value)) { + setUtelError(false); + } else { + setUtelError(true); + setTelMessage("전화번호가 유효하지 않습니다."); + } + }, []); + function isTel(tel) { + // 010XXXXYYY, 010XXXYYYY + const telRegex = /^[0-9]{3}[0-9]{3,4}[0-9]{4}$/; + if (telRegex.test(tel)) { + return true; + } + return false; + } + + /* 닉네임 */ + const onChangeNickname = useCallback(async (e) => { + setUnickname(e.target.value); + var link = "http://localhost:8080/signup"; + try { + await axios + .get(link, { + nickname: e.target.value, + }) + .then((res) => { + console.log(res); + if (res.status === 200) { + alert("통신 성공"); + } + }); + } catch {} + }); + + useEffect(() => { + if (checkList.includes("terms") && checkList.includes("collect")) { + // 하나라도 true면 button은 false + // 모두 false여야 button이 true + if ( + uemailError || + upwLengthError || + upwLetterError || + ubirthError || + utelError || + unameError + ) { + setButtonColor(false); + } else { + setButtonColor(true); + } + } else { + setButtonColor(false); + } + }, [ + checkList, + uemailError, + upwLengthError, + upwLetterError, + ubirthError, + utelError, + unameError, + ]); + + const onClickRegister = useCallback( + async (e) => { + if (checkList.includes("marketing")) { + setUconsent(true); + } + alert("성공"); + e.preventDefault(); + var link = "http://localhost:8080/signup"; + try { + await axios + .post(link, { + email: uemail, + password: upw, + name: uname, + sex: usex, + birth: ubirth, + phoneNumber: utel, + termConsent: uconsent, + }) + .then((res) => { + console.log(res); + if (res.status === 200) { + // 성공 + alert("성공"); + } else { + alert("통신은 됐는데 실패"); + } + }); + } catch (err) { + alert("실패"); + console.log(err); + } + }, + [uemail, upw, uname, usex, ubirth, utel, uconsent] + ); + + const onClickDuplicate = () => {}; + + return ( +
+ {/* 회원가입 안내 */} +

+ 무료 회원가입 +

+
+
    +
  • 다양한 카테고리에 속한 많은 작품들을 확인할 수 있어요
  • +
  • 내 작품의 피드백을 받아보고 또 피드백을 전달할 수 있어요
  • +
  • 필요한 작업물을 관리하고 스크랩할 수 있어요
  • +
+
+ + {/* 회원가입 입력창 */} +
+ + + {/* 이메일 */} + + + + + {/* 비밀번호 */} + + + + + {/* 비밀번호 확인 */} + + + + + {/* 이름 */} + + + + + {/* 생년월일 */} + + + + + {/* 성별 */} + + + + + {/* 연락처 */} + + + + + {/* 닉네임 */} + + + + + +
+ + + + {uemail.length > 0 && ( + + {uemailError ? emailMessage : ""} + + )} +
+ + + + {upw.length > 0 && ( + + {upwLengthError ? pwLengthMessage : ""} + {upwLengthError ?
: ""} + + {upwLetterError ? pwLetterMessage : ""} +
+ )} +
+ + + + {confirmupw.length > 0 && ( + + {confirmupwError ? confirmpwMessage : ""} + + )} +
+ + + + {uname.length > 0 && ( + + {unameError ? nameMessage : ""} + + )} +
+ + +
+
+ {/* 년 */} + {" "} + 년 +
+
+ {/* 월 */} + {" "} + 월 +
+ +
+ {/* 일 */} + {" "} + 일 +
+
+ {ubirth.length > 0 && ( + + {ubirthError ? birthMessage : ""} + + )} +
+ + +
+
+ + +
+
+ + +
+
+
+ + + + {utel.length > 0 && ( + + {utelError ? telMessage : ""} + + )} +
+ + +
+ +
+
+ +
+
+
+ + {/* 동의 */} + + + + {/* 전체 동의 */} +
+
+ +
+
+ } + checkedIcon={} + id="1" + style={{ transform: "scale(1.3)" }} + name="all" + onChange={checkAll} + checked={checkList.length === 3 ? true : false} + /> + } + labelPlacement="start" + /> +
+
+ + {/*이용 약관*/} +
+
+ +
+
+ } + checkedIcon={} + id="2" + style={{ transform: "scale(1.3)" }} + name="terms" + onChange={check} + checked={checkList.includes("terms") ? true : false} + /> + } + labelPlacement="start" + /> +
+
+ + {/*개인 정보 수집 및 처리 방침*/} +
+
+ +
+
+ } + checkedIcon={} + id="3" + style={{ transform: "scale(1.3)" }} + name="collect" + onChange={check} + checked={checkList.includes("collect") ? true : false} + /> + } + labelPlacement="start" + /> +
+
+ + {/*정보 및 이벤트성 이메일 수신 동의*/} +
+
+ +
+
+ } + checkedIcon={} + id="4" + style={{ transform: "scale(1.3)" }} + name="marketing" + onChange={check} + checked={checkList.includes("marketing") ? true : false} + /> + } + labelPlacement="start" + /> +
+
+
+
+
+ +
+ ); +} + +export default SignUpContainer; diff --git a/FE/src/containers/signup/SingUp.module.css b/FE/src/containers/signup/SingUp.module.css new file mode 100644 index 0000000..cacb45b --- /dev/null +++ b/FE/src/containers/signup/SingUp.module.css @@ -0,0 +1,158 @@ +.input { + width: 100%; + height: 43px; + font-size: 20px; + border: 1px solid #B0B0B0; + outline: none; + margin: 10px; + background: #FFFFFF; + text-align: center; + border-radius: 10px; +} + +input::-webkit-input-placeholder { + color: #B0B0B0; + font-size: 0.8rem; +} +input:-ms-input-placeholder { + color: #B0B0B0;font-size: 15px; +} +textarea::-webkit-input-placeholder { + color: #B0B0B0;font-size: 15px; +} +textarea:-ms-input-placeholder { + color: #B0B0B0;font-size: 15px; +} + +button{ + width: 300px; + height: 45px; + + background: #FFF48C; + border-radius: 10px; + border:#FFF48C; + font-family: "NotoSansKR-400"; + font-size: 15px; + text-align: center; + cursor: pointer; +} + +/* SECTION - BIRTH */ +select { + width: 70%; + height: 43px; + box-sizing: border-box; + text-align: center; + border-radius: 10px; + border: 1px solid #B0B0B0; + color: #383838; + background-color: #ffffff; + /*margin-left: 10px;*/ + margin: 10px 0px 10px 10px; +} + +.form_radio_btn { + width: 47%; + border: 1px solid #B0B0B0; + height: 43px; + border-radius: 10px; + display: inline-block; + +} + +.form_radio_btn input[type=radio] { + display: none; +} + +.form_radio_btn label { + display: block; cursor: pointer; + border-radius: 10px; + font-size: 15px; + margin: 0 auto; + text-align: center; + height: -webkit-fill-available; + line-height: 45px; +} + +/* Checked */ +.form_radio_btn input[type=radio]:checked + label { + background: #FADA5E; + color: #fff; + box-shadow: none; +} + + +/* Disabled */ +.form_radio_btn input[type=radio] + label { + background: #F9FAFC; + color: #B0B0B0; + box-shadow: 0px 4px 4px rgba(0, 0, 0, 0.25); +} + +/* table */ +table { + justify-content: center; + align-items: center; + /*display: flex;*/ + width:90%; + font-family: "NotoSansKR-400"; + font-size: 20px; + /*padding: 10px;*/ + margin:10px; +} + +tbody{ + width: 80%; +} + +th{ + width:20%; + text-align: right; +} + +.td{ + width:80%; +} + +.terms{ + text-align: left; +} + +button{ + background: #FADA5E; + color: #fff; + width:100%; + +border: 1px solid #D0D0D0; +box-shadow: 0px 4px 4px rgba(0, 0, 0, 0.25); +border-radius: 30px; +} + +.agree { + font-family: "NotoSansKR-400"; + font-size: 1rem; + font-weight: 400; + width: 100%; + height: 30px;/*//70px;*/ + justify-content: center; + align-items: center; + display: flex; + padding: 20px; +} + +.left { + float: left; + width: 80%; + align-items: center; + display: flex; + text-align: left; +} + +.right{ + float: left; + width: 20%; + text-align: right; +} +.display-enter { + white-space: pre-line; +} \ No newline at end of file diff --git a/FE/src/containers/sociallogin/KakaoLogin.jsx b/FE/src/containers/sociallogin/KakaoLogin.jsx new file mode 100644 index 0000000..183d0b2 --- /dev/null +++ b/FE/src/containers/sociallogin/KakaoLogin.jsx @@ -0,0 +1,54 @@ +import React, { useEffect } from "react"; +import { useLocation, useNavigate } from "react-router-dom"; +import { CLIENT_ID, KAKAO_AUTH_URL, REDIRECT_URL } from "./kakaodata"; +import axios from "axios"; + +function KakaoLogin() { + const navigate = useNavigate(); + + const sendToken = () => { + // back에 인가 코드 보내기 + const config = { "Conteny-Type": "application/json" }; + const TokenForm = { code: sessionStorage.getItem("kakaotoken") }; + console.log(TokenForm); + axios + .get(`/login/kakao?code=${sessionStorage.getItem("kakaotoken")}`) + .then((res) => { + console.log(res); + if (res.status === 200) { + console.log(res); + } + navigate("/"); + }) + .catch((err) => { + console.log(err); + alert("로그인 실패"); + navigate("/sociallogin"); + }); + }; + + useEffect(() => { + let params = new URL(document.location.toString()).searchParams; + let code = params.get("code"); + let grant_type = "authorization_code"; + + axios + .post( + `https://kauth.kakao.com/oauth/token?grant_type=${grant_type}&client_id=${CLIENT_ID}&redirect_uri=${REDIRECT_URL}&code=${code}`, + { + headers: { + "Content-type": "application/x-www-form-urlencoded;charset=utf-8", + }, + } + ) + .then((res) => { + console.log(res); + console.log(typeof res.data.access_token); + sessionStorage.setItem("kakaotoken", res.data.access_token); + sendToken(); + }); + }); + return
; +} + +export default KakaoLogin; diff --git a/FE/src/containers/sociallogin/SocialLoginContainer.jsx b/FE/src/containers/sociallogin/SocialLoginContainer.jsx new file mode 100644 index 0000000..d2145f4 --- /dev/null +++ b/FE/src/containers/sociallogin/SocialLoginContainer.jsx @@ -0,0 +1,115 @@ +import React from "react"; +import { useNavigate } from "react-router"; +import styled from "styled-components"; +import kakao_login from "../../images/kakao_login_large.png"; +import { KAKAO_AUTH_URL } from "./kakaodata"; +import axios from "axios"; +import { useEffect } from "react"; + +const Style = { + Container: styled.div` + box-sizing: border-box; + width: 450px; //100%; + height: 60px; + background: #f8f8f8; + border-radius: 30px; + font-family: "NotoSansKR-700"; + text-align: center; + display: flex; + justify-content: center; + align-items: center; + margin: 40px auto; + font-family: NotoSansKR-700; + `, + + Wrapper: styled.div` + width: 450px; + margin: 0px auto; + font-family: NotoSansKR-400; + font-weight: 500; + `, +}; + +const { Kakao } = window; + +function SocialLoginContainer() { + const code = new URL(window.location.href).searchParams.get("code"); + + const navigate = useNavigate(); + + /*const getSession = () => { + const config = { "Conteny-Type": "application/json" }; + axios + .post("/signup/kakao", code, config) + .then((result) => { + console.log(result); + if (result.status === 200 && result.data !== "login fail") { + } + navigate("/"); + }) + .catch((err) => { + console.log(err); + alert("로그인에 실패하였습니다."); + navigate("/sociallogin"); + }); + };*/ + + /*const kakaoLoginHandler = () => { + Kakao.Auth.login({ + success: function (authObj) { + fetch(`${KAKAO_AUTH_URL}`, { + method: "GET", + body: JSON.stringfy({ + access_token: authObj.access_token, + }), + }) + .then((res) => { + console.log(res); + }) + .catch((err) => { + console.log(err); + }); + }, + }); + };*/ + return ( +
+
+ 간단한 로그인으로
공모전 관련 자료를 자유롭게 찾아보세요 +
+ + + 📚 공모전 수상작을 포함한 참가 작품들을 자유롭게 열람 + + + 💡참가작 공유를 통한 다양한 피드백 및 코멘트 수령 + + + + + 카카오 계정으로 3초만에 가입하기 + + + kakaologin + + +
+ ); +} + +export default SocialLoginContainer; diff --git a/FE/src/containers/sociallogin/SocialLoginContainer.module.css b/FE/src/containers/sociallogin/SocialLoginContainer.module.css new file mode 100644 index 0000000..e69de29 diff --git a/FE/src/containers/sociallogin/kakaodata.js b/FE/src/containers/sociallogin/kakaodata.js new file mode 100644 index 0000000..0ef59c8 --- /dev/null +++ b/FE/src/containers/sociallogin/kakaodata.js @@ -0,0 +1,4 @@ +export const CLIENT_ID=process.env.REACT_APP_REST_API_KEY +export const REDIRECT_URL='http://localhost:8080/login/kakao' + +export const KAKAO_AUTH_URL=`https://kauth.kakao.com/oauth/authorize?client_id=${CLIENT_ID}&redirect_uri=${REDIRECT_URL}&response_type=code` \ No newline at end of file diff --git a/FE/src/images/kakako_login.png b/FE/src/images/kakao_login_large.png similarity index 100% rename from FE/src/images/kakako_login.png rename to FE/src/images/kakao_login_large.png diff --git a/FE/src/images/kakao_login_small.png b/FE/src/images/kakao_login_small.png new file mode 100644 index 0000000..f7f6776 Binary files /dev/null and b/FE/src/images/kakao_login_small.png differ diff --git a/FE/src/index.js b/FE/src/index.js index 1264ca3..ff60ded 100644 --- a/FE/src/index.js +++ b/FE/src/index.js @@ -5,7 +5,8 @@ import App from './App'; import reportWebVitals from './reportWebVitals'; import styled from "styled-components"; import { BrowserRouter, Route, Routes } from 'react-router-dom'; -import LoginPage from './page/LoginPage'; +import LoginPage from './page/login/LoginPage'; +import SocialLoginPage from './page/sociallogin/SocialLoginPage'; import UnknownPage from './page/UnknownPage'; import RefIdeaPage from './page/reference/RefIdeaPage' @@ -23,6 +24,11 @@ import PageFollowing from './page/mypage/PageFollowing' import PageScrap from './page/mypage/PageScrap'; import PageMyFeedback from './page/mypage/PageMyFeedback'; import PageFAQ from './page/mypage/PageFAQ'; +import SignUpPage from './page/SignUpPage'; + +import EmailFind from './page/findinfo/EmailFind'; +import PasswordFind from './page/findinfo/PasswordFind'; +import KakaoLogin from './containers/sociallogin/KakaoLogin'; const root = ReactDOM.createRoot(document.getElementById('root')); const Style = { @@ -39,6 +45,7 @@ root.render( } /> + }/> } /> } /> @@ -67,4 +74,4 @@ root.render( // If you want to start measuring performance in your app, pass a function // to log results (for example: reportWebVitals(console.log)) // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals -reportWebVitals(); +reportWebVitals(); \ No newline at end of file diff --git a/FE/src/layout/Header/Header.jsx b/FE/src/layout/Header/Header.jsx index 4ce01a0..86349ad 100644 --- a/FE/src/layout/Header/Header.jsx +++ b/FE/src/layout/Header/Header.jsx @@ -1,7 +1,7 @@ -import { useEffect, useRef, useState} from "react"; -import styles from './Header.module.css'; +import { useEffect, useRef, useState } from "react"; +import styles from "./Header.module.css"; import styled from "styled-components"; -import NotificationsNoneIcon from '@mui/icons-material/NotificationsNone'; +import NotificationsNoneIcon from "@mui/icons-material/NotificationsNone"; import { useNavigate } from "react-router-dom"; const Style = { @@ -12,12 +12,12 @@ const Style = { position: fixed; top: 0; justify-content: space-between; - background-color: #FFF48C; + background-color: #fff48c; `, Logo: styled.div` width: 224px; height: 80px; - background-color: #D3A8FF; + background-color: #d3a8ff; display: flex; align-items: center; justify-content: center; @@ -26,7 +26,7 @@ const Style = { font-weight: bold; font-size: 20px; cursor: pointer; - margin-right:0px; + margin-right: 0px; `, NavWrapper: styled.div` width: 1550px; @@ -35,144 +35,170 @@ const Style = { margin-right: 75px; height: 80px; gap: 85px; - position:relative; + position: relative; `, NavItemWrapper: styled.div` - position:relative; - left:0%; + position: relative; + left: 0%; `, NavItem: styled.span` color: black; font-weight: bold; font-size: 20px; cursor: pointer; - margin-right:30px; + margin-right: 30px; `, ButtonWrapper: styled.div` - display:flex; - flex-direction:row; - position:absolute; - right:0%; + display: flex; + flex-direction: row; + position: absolute; + right: 0%; `, BellButton: styled.button` border: solid 2px white; width: 3rem; - height : 3rem; - background: #FFFFFF; + height: 3rem; + background: #ffffff; border-radius: 50px; cursor: pointer; - position:relative; + position: relative; `, BellNotice: styled.div` - position:absolute; + position: absolute; width: 10px; - height : 10px; - background: #F27A7A; + height: 10px; + background: #f27a7a; border-radius: 50%; border: solid 0.5px white; - left:28px; - top:7px; - visibility: hidden; + left: 28px; + top: 7px; `, //visibility: hidden;으로 현재 알림창의 표시 가림 - StyledButton: styled.button` - width: 10rem; - height : 3rem; - background-color: white; - color: #464646; - font-size: 15px; - border: solid 2px white; - cursor: pointer; - letter-spacing: 0.2em; - font-weight: 550; - display: flex; - align-items : center; - justify-content: center; - background: #FFFFFF; - box-shadow: 0px 4px 4px rgba(0, 0, 0, 0.25); - border-radius: 50px; - margin-left:10px; + StyledButton: styled.button` + width: 10rem; + height: 3rem; + background-color: white; + color: #464646; + font-size: 15px; + border: solid 2px white; + cursor: pointer; + letter-spacing: 0.1em; + font-weight: 550; + display: flex; + align-items: center; + justify-content: center; + background: #ffffff; + box-shadow: 0px 4px 4px rgba(0, 0, 0, 0.25); + border-radius: 50px; + margin-left: 10px; `, - HoverlineUnder1: styled.div` - width:75px; - border:2px solid #FFFFFF; - position:absolute; - top:33px; - left:3px; + HoverlineUnder1: styled.div` + width: 75px; + border: 2px solid #ffffff; + position: absolute; + top: 33px; + left: 3px; `, - HoverlineUnder2: styled.div` - width:100px; - border:2px solid #FFFFFF; - position:absolute; - top:33px; - left:114px; + HoverlineUnder2: styled.div` + width: 100px; + border: 2px solid #ffffff; + position: absolute; + top: 33px; + left: 114px; `, - HoverlineUnder3: styled.div` - width:95px; - border:2px solid #FFFFFF; - position:absolute; - top:33px; - left:249px; + HoverlineUnder3: styled.div` + width: 95px; + border: 2px solid #ffffff; + position: absolute; + top: 33px; + left: 249px; `, }; const Header = () => { - const Navigate=useNavigate(); + const Navigate = useNavigate(); const refOnClick = () => { - Navigate('/') + Navigate("/"); }; const taskOnClick = () => { - Navigate('/manage/list') + Navigate("/manage/list"); }; const myOnClick = () => { - Navigate('/mypage/profile') + Navigate("/mypage/profile"); }; - const noticeOnClick = () => { - - } + const noticeOnClick = () => {}; const loginOnClick = () => { - Navigate('/login') - } + Navigate("/login"); + }; + + const bellOnClick = () => {}; - const bellOnClick = () => { - - } - // + const shareOnClick = () => {}; + + const signupOnClick = () => { + Navigate("/signup"); + }; + + const loginYN = false; + //구체적인 로그인 여부 작업 전이라 임시적으로 만든 변수로 boolean 값에 따라 다른 헤더 출력 return (
- - 레모아 로고 - + 레모아 로고 레퍼런스 작업물 관리 마이페이지 - {(window.location.pathname.includes('/ref') || window.location.pathname=='/') - ?:""} - {(window.location.pathname.includes('/manage'))?:""} - {(window.location.pathname.includes('/mypage'))?:""} + {window.location.pathname.includes("/ref") || + window.location.pathname == "/" ? ( + + ) : ( + "" + )} + {window.location.pathname.includes("/manage") ? ( + + ) : ( + "" + )} + {window.location.pathname.includes("/mypage") ? ( + + ) : ( + "" + )} - - - - - - 로그인 - + {loginYN ? ( + <> + + + + + + 작업물 공유하기 + + + ) : ( + <> + + 로그인 + + + 회원가입 + + + )} -
- -
); }; -export default Header; \ No newline at end of file +export default Header; diff --git a/FE/src/layout/Layout.module.css b/FE/src/layout/Layout.module.css index 549deda..4a5d86e 100644 --- a/FE/src/layout/Layout.module.css +++ b/FE/src/layout/Layout.module.css @@ -12,10 +12,10 @@ .main{ min-height: 1024px; - width: 100%; + /*width: 100%; /*height: 100%; /* 이렇게 해야 footer가 하단 고정 */ background-color: white; - + overflow: auto; /* 2023-01-24 scroll 기능 구현 */ display: flex; justify-content: center; align-items: center; diff --git a/FE/src/page/LoginPage.jsx b/FE/src/page/LoginPage.jsx deleted file mode 100644 index 1779e34..0000000 --- a/FE/src/page/LoginPage.jsx +++ /dev/null @@ -1,13 +0,0 @@ -import React from 'react' -import LoginContainer from '../containers/login/LoginContainer' -import Layout from '../layout/Layout' - -function LoginPage() { - return ( - - - - ) -} - -export default LoginPage diff --git a/FE/src/page/SignUpPage.jsx b/FE/src/page/SignUpPage.jsx new file mode 100644 index 0000000..e0ef3a2 --- /dev/null +++ b/FE/src/page/SignUpPage.jsx @@ -0,0 +1,13 @@ +import React from "react"; +import SignUpContainer from "../containers/signup/SignUpContainer"; +import Layout from "../layout/Layout"; + +function SignUpPage() { + return ( + + + + ); +} + +export default SignUpPage; diff --git a/FE/src/page/findinfo/EmailFind.jsx b/FE/src/page/findinfo/EmailFind.jsx new file mode 100644 index 0000000..5fcda83 --- /dev/null +++ b/FE/src/page/findinfo/EmailFind.jsx @@ -0,0 +1,11 @@ +import Layout from "../../layout/Layout"; +import React from "react"; +import EmailFindDe from "../../containers/findinfo/EmailFindDe"; +function EmailFind() { + return ( + + + + ) +} +export default EmailFind; \ No newline at end of file diff --git a/FE/src/page/findinfo/PasswordFind.jsx b/FE/src/page/findinfo/PasswordFind.jsx new file mode 100644 index 0000000..809057e --- /dev/null +++ b/FE/src/page/findinfo/PasswordFind.jsx @@ -0,0 +1,12 @@ +import Layout from "../../layout/Layout"; +import React from "react"; +import PasswordFindDe from "../../containers/findinfo/PasswordFindDe"; + +function PasswordFind() { + return ( + + + + ) +} +export default PasswordFind; \ No newline at end of file diff --git a/FE/src/page/login/LoginPage.jsx b/FE/src/page/login/LoginPage.jsx new file mode 100644 index 0000000..cdf48c4 --- /dev/null +++ b/FE/src/page/login/LoginPage.jsx @@ -0,0 +1,13 @@ +import React from "react"; +import LoginContainer from "../../containers/login/LoginContainer"; +import Layout from "../../layout/Layout"; + +function LoginPage() { + return ( + + + + ); +} + +export default LoginPage; diff --git a/FE/src/page/sociallogin/SocialLoginPage.jsx b/FE/src/page/sociallogin/SocialLoginPage.jsx new file mode 100644 index 0000000..8c3644f --- /dev/null +++ b/FE/src/page/sociallogin/SocialLoginPage.jsx @@ -0,0 +1,12 @@ +import React from "react"; +import Layout from "../../layout/Layout"; +import SocialLoginContainer from "../../containers/sociallogin/SocialLoginContainer"; +function SocialLoginPage() { + return ( + + + + ); +} + +export default SocialLoginPage; diff --git a/__MACOSX/BE/.DS_Store b/__MACOSX/BE/.DS_Store new file mode 100644 index 0000000..0caa607 Binary files /dev/null and b/__MACOSX/BE/.DS_Store differ