Backend
home
🕠

2024. 9. 5 (rest-api)

생성일
2025/01/24 05:52
태그

rest-api 관련 실습 진행 (blog 예제)

react
링크 테이블 추가 후 코드 내용
import { Box, Button, FormControl, FormLabel, Grid2, Paper, Stack, styled, Switch, TextField, Typography } from "@mui/material"; import { useForm } from "react-hook-form"; import { favAPI } from "../../api/services/favorite"; import { useLocation, useNavigate, useParams } from "react-router-dom"; import { useEffect, useRef, useState } from "react"; const FavForm = () => { // const { favId } = useParams(); const { state } = useLocation(); const navigate = useNavigate(); const { register, formState: { errors }, handleSubmit, setValue, } = useForm(); const [uploadFile, setUploadFile] = useState(); const imgRef = useRef(); const uploadFilePreview = () => { const reader = new FileReader(); reader.readAsDataURL(imgRef.current.files[0]); reader.onloadend = () => { setUploadFile(reader.result); }; }; useEffect(() => { if (state) { state.image && setShowFileInput(false); setValue("title", state.title); setValue("url", state.url); } }, []); const [showFileInput, setShowFileInput] = useState(true); const toggleFileInput = () => { setShowFileInput(prev => !prev); } const onSubmit = async (data) => { try { const formData = new FormData(); Object.keys(data).forEach(key => { formData.append(key, data[key]); }); if (data.logo.length) { formData.append("image", data.logo[0]); } else { formData.delete("image"); if (showFileInput) { formData.append("deleteImage", true); } } if (state) { formData.append("id", state.id); const res = await favAPI.modifyFav(formData); } else { const res = await favAPI.writeFav(formData); } navigate("/favorite") } catch (error) { console.error(error); } } return ( <Paper elevation={3} sx={{ padding: 4, maxWidth: 600, margin: "auto", marginTop: 4, }} > <Box component="form" onSubmit={handleSubmit(onSubmit)} noValidate> <Grid2 container spacing={3}> <FormControl fullWidth> <FormLabel htmlFor="title">사이트 이름</FormLabel> <TextField autoComplete="title" {...register("title", { required: true })} fullWidth id="title" placeholder="구글" error={errors.title ? true : false} helperText={errors.title && "사이트 이름은 필수값입니다."} /> </FormControl> <FormControl fullWidth> <FormLabel htmlFor="url">URL</FormLabel> <TextField id="url" fullWidth {...register("url", { required: true, pattern: /((?:https\:\/\/)|(?:http\:\/\/)|(?:www\.))?([a-zA-Z0-9\-\.]+\.[a-zA-Z]{2,3}(?:\??)[a-zA-Z0-9\-\._\?\,\'\/\\\+&%\$#\=~]+)/ })} autoComplete="url" placeholder="www.google.com" variant="outlined" error={errors.url ? true : false} helperText={errors.url && "주소 규격에 맞지 않습니다."} /> </FormControl> <FormControl fullWidth> <FormLabel component="legend">로고 이미지</FormLabel> {state?.image && ( <Stack direction="row" spacing={1} sx={{ alignItems: "center", marginBottom: 1 }} > <Typography>변경</Typography> <AntSwitch defaultChecked onClick={toggleFileInput} /> <Typography>유지</Typography> </Stack> )} {showFileInput && <> <TextField type="file" {...register("logo")} slotProps={{ htmlInput : {"accept": "image/*"}}} variant="outlined" inputRef={imgRef} onChange={uploadFilePreview} /> </> } { (!showFileInput || uploadFile) && <Box sx={{ textAlign: "center", marginTop: 2 }}> <img src={uploadFile ? uploadFile : `${process.env.REACT_APP_SERVER}/img/${state.image.saved}`} alt="Current Logo" style={{ maxWidth: "100%", maxHeight: 200 }} /> </Box> } </FormControl> <Button type="submit" fullWidth variant="contained" > {state ? "수정하기" : "등록하기"} </Button> </Grid2> </Box> </Paper> ); } const AntSwitch = styled(Switch)(({ theme }) => ({ width: 36, height: 20, padding: 0, display: "flex", "&:active": { "& .MuiSwitch-thumb": { width: 15, }, "& .MuiSwitch-switchBase.Mui-checked": { transform: "translateX(16px)", }, }, "& .MuiSwitch-switchBase": { padding: 2, "&.Mui-checked": { transform: "translateX(16px)", color: "#fff", "& + .MuiSwitch-track": { backgroundColor: "#1890ff", opacity: 1, }, }, }, "& .MuiSwitch-thumb": { width: 16, height: 16, borderRadius: 8, transition: theme.transitions.create(["width"], { duration: 200, }), }, "& .MuiSwitch-track": { borderRadius: 10, backgroundColor: "rgba(0,0,0,.25)", opacity: 1, }, })); export default FavForm;
JavaScript
복사
FavList
import { useNavigate } from "react-router-dom"; import { Box, Button } from "@mui/material"; import { useEffect, useReducer, useState } from "react"; import { favAPI } from "../../api/services/favorite"; import FavAccordion from "./FavAccordion"; const favListReducer = (state, action) => { switch (action.type) { case "SET_FAVS": return action.payload; case "DELETE_FAV": return state.filter(fav => fav.id != action.payload.id); } } const FavList = () => { const [favList, dispatch] = useReducer(favListReducer, []); const navigate = useNavigate(); const getFavList = async () => { const res = await favAPI.getFavList(); dispatch({type: "SET_FAVS", payload: res.data}); }; useEffect(() => { getFavList(); }, []); return ( <> <Button onClick={() => navigate("/favorite/write")}>즐겨찾기 추가</Button> <Box sx={{width: "500px"}}> {favList.map((fav) => ( <FavAccordion key={fav.id} fav={fav} dispatch={dispatch}/> ))} </Box> </> ); }; export default FavList;
JavaScript
복사
FavAccordion
import { Accordion, AccordionDetails, AccordionSummary, Avatar, Box, Button, styled, Typography, } from "@mui/material"; import ArrowForwardIosSharpIcon from "@mui/icons-material/ArrowForwardIosSharp"; import { useState } from "react"; import Swal from "sweetalert2"; import { favAPI } from "../../api/services/favorite"; import { useNavigate } from "react-router-dom"; const FavAccordion = ({fav, dispatch}) => { const navigate = useNavigate(); const [isExpand, setIsExpand] = useState(false); const handleExpand = () => setIsExpand(prev=> !prev); const handleMove = () => { let urlLink = fav.url; if (!fav.url.startsWith("http://") && !fav.url.startsWith("https://")) { urlLink = `http://${fav.url}`; } window.open(urlLink, "_blank", "noopener, noreferrer"); } const handleDelete = () => { Swal.fire({ title: "확실해요?", text: "한 번 삭제하면 다시 되돌릴 수 없어요", showCancelButton: true, confirmButtonColor: "#3085d6", cancelButtonColor: "#d33", confirmButtonText: "응! 지워줘." }).then(async (result) => { if (result.isConfirmed) { try { const res = await favAPI.deleteFav(fav.id); if (res.status == 200) { Swal.fire({ text: "삭제되었습니다.", icon: "success" }); dispatch({type:"DELETE_FAV", payload: fav}) } } catch (err) { console.error(err); } } }); } const handleModify = () => { navigate(`/favorite/modify/${fav.id}`, { state : fav }); } console.log(fav); return ( <MyAccordion expanded={isExpand} onClick={handleExpand}> <MyAccordionSummary> <Box sx={{display: "flex", justifyContent: "space-between", width: "100%"}}> <Avatar alt={fav.title} src={`${process.env.REACT_APP_SERVER}/img/${fav.image?.saved}`} /> <span>{fav.id}</span> <span style={{width: "40%"}}>{fav.title}</span> <Button onClick={handleModify}>수정</Button> <Button onClick={handleDelete}>삭제</Button> </Box> </MyAccordionSummary> <MyAccordionDetails> <Button onClick={handleMove}>{fav.url}</Button> </MyAccordionDetails> </MyAccordion> ); }; const MyAccordion = styled((props) => ( <Accordion disableGutters elevation={0} square {...props} /> ))(({ theme }) => ({ border: `1px solid ${theme.palette.divider}`, "&:not(:last-child)": { borderBottom: 0, }, "&::before": { display: "none", }, })); const MyAccordionSummary = styled((props) => ( <AccordionSummary expandIcon={<ArrowForwardIosSharpIcon sx={{ fontSize: "0.9rem" }} />} {...props} /> ))(({ theme }) => ({ backgroundColor: "rgba(0, 0, 0, .03)", flexDirection: "row-reverse", "& .MuiAccordionSummary-expandIconWrapper.Mui-expanded": { transform: "rotate(90deg)", }, "& .MuiAccordionSummary-content": { marginLeft: theme.spacing(1), }, ...theme.applyStyles("dark", { backgroundColor: "rgba(255, 255, 255, .05)", }), })); const MyAccordionDetails = styled(AccordionDetails)(({ theme }) => ({ padding: theme.spacing(2), borderTop: "1px solid rgba(0, 0, 0, .125)", })); export default FavAccordion;
JavaScript
복사
springboot - 회원가입 인증 관련 내용 진행
AuthController
package com.kosta.controller; import java.util.List; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PatchMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import com.kosta.domain.SignUpRequest; import com.kosta.domain.UserDeleteRequest; import com.kosta.domain.UserResponse; import com.kosta.domain.UserUpdateRequest; import com.kosta.service.AuthService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @Slf4j @RestController @RequestMapping("/api/auth") @RequiredArgsConstructor public class AuthController { private final AuthService authService; // 회원가입 @PostMapping("") public ResponseEntity<UserResponse> signUp(@RequestBody SignUpRequest signUpRequest) { log.info("[signUp] 회원가입 진행. 요청정보 : {}", signUpRequest); UserResponse userResponse = authService.signUp(signUpRequest); return ResponseEntity.status(HttpStatus.CREATED).body(userResponse); } // 회원 전체 리스트 @GetMapping("") public ResponseEntity<List<UserResponse>> getUserList() { log.info("[getUserList] 회원 전체 조회"); List<UserResponse> userList = authService.getUserList(); return ResponseEntity.ok(userList); } // 회원 정보 수정 @PatchMapping("") public ResponseEntity<UserResponse> updateUser(@RequestBody UserUpdateRequest userUpdateReqeust) { log.info("[updateUser] 회원 정보 수정. 수정 요청 정보 : {}", userUpdateReqeust); UserResponse userResponse = authService.updateUser(userUpdateReqeust); return ResponseEntity.ok(userResponse); } // 회원 삭제 @DeleteMapping("") public ResponseEntity<?> userWithdrawal(@RequestBody UserDeleteRequest userDeleteRequest) { log.info("[updateUser] 회원 삭제. 삭제 요청 정보 : {}", userDeleteRequest); authService.deleteUser(userDeleteRequest); return ResponseEntity.ok(null); } }
Java
복사
AuthService
package com.kosta.service; import java.util.List; import com.kosta.domain.SignUpRequest; import com.kosta.domain.UserDeleteRequest; import com.kosta.domain.UserResponse; import com.kosta.domain.UserUpdateRequest; public interface AuthService { UserResponse signUp(SignUpRequest signUpRequest); List<UserResponse> getUserList(); UserResponse updateUser(UserUpdateRequest userUpdateReqeust); void deleteUser(UserDeleteRequest userDeleteRequest); }
Java
복사
AuthServiceImpl
package com.kosta.service; import java.util.List; import org.springframework.stereotype.Service; import com.kosta.domain.SignUpRequest; import com.kosta.domain.UserDeleteRequest; import com.kosta.domain.UserResponse; import com.kosta.domain.UserUpdateRequest; import com.kosta.entity.User; import com.kosta.repository.UserRepository; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @Slf4j @Service @RequiredArgsConstructor public class AuthServiceImpl implements AuthService { private final UserRepository userRepository; @Override public UserResponse signUp(SignUpRequest signUpRequest) { User user = User.builder().email(signUpRequest.getEmail()).name(signUpRequest.getName()) .password(signUpRequest.getPassword()).build(); User savedUser = userRepository.save(user); return UserResponse.toDTO(savedUser); } @Override public List<UserResponse> getUserList() { List<User> userList = userRepository.findAll(); return userList.stream().map(UserResponse::toDTO).toList(); } @Override public UserResponse updateUser(UserUpdateRequest userUpdateReqeust) { User user = userRepository.findByEmail(userUpdateReqeust.getEmail()) .orElseThrow(() -> new IllegalArgumentException("회원 정보 조회에 실패했습니다. [없는 이메일]")); if (!user.getPassword().equals(userUpdateReqeust.getPassword())) { throw new RuntimeException("비밀번호 입력 오류"); } if (userUpdateReqeust.getName() != null) user.setName(userUpdateReqeust.getName()); User updatedUser = userRepository.save(user); return UserResponse.toDTO(updatedUser); } @Override public void deleteUser(UserDeleteRequest userDeleteRequest) { User user = userRepository.findByEmail(userDeleteRequest.getEmail()) .orElseThrow(() -> new IllegalArgumentException("회원 정보 조회에 실패했습니다. [없는 이메일]")); if (!user.getPassword().equals(userDeleteRequest.getPassword())) { throw new RuntimeException("비밀번호 입력 오류"); } userRepository.delete(user); } }
Java
복사