React
bmsapi 프로젝트 - bmsfront 리액트 (member 패키지)
bumkee25
2023. 4. 24. 17:42
순서: insert - list/comp - detail - update - delete 작업
1. 서로 다른 사이트간의 통신
- bmsapi -> http://localhost:9005
- bmsfront -> http://localhost:3000
- bmsapi 프로젝트의 기본패키지에 WebMvcConfigurer를 상속하는 클래스 생성
@Configuration
public class WebMvcConfiguerImpl implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("http://localhost:3000")
.allowedMethods("GET", "POST", "PUT", "DELETE")
.allowedHeaders("*")
.allowCredentials(true)
.maxAge(3600);
}
}
2. MainEx.js
function MainEx() {
return (
<div>
<p>MainEx</p>
<p><Link to={"/member/list"}>회원 목록</Link></p>
<p><Link to={"/board/list"}>글 목록</Link></p>
</div>
)
}
3. NetworkUtils.js
- fetch구문 미리 작업 후 나중에 필요할 때 사용(member, board)
export function fetchFn(method, url, dto){
let options = {
method : method,
headers : {
"Content-Type" : "application/json"
}
};
if(dto){
options.body = JSON.stringify(dto);
}
return fetch(url, options)
.then(res => {
if(!res.ok){
throw new Error("작업 실패");
}
return res.json();
})
.catch(error => {
alert(error.message);
});
}
export function insertFetchfn(servicename, dto){
return fetchFn("POST", `http://localhost:9005/api/${servicename}`, dto)
.then(data =>{
console.log(data.result);
window.location.href = `/${servicename}/detail/${data.result.username}`
})
}
export function InsertFetchfn(servicename, dto){
return fetchFn("POST", `http://localhost:9005/api/${servicename}`, dto)
.then(data =>{
console.log(data.result);
window.location.href = `/${servicename}/detail/${data.result.id}`
})
}
4. App.js
function App() {
return (
<div className="App">
<header>여기는 나중에 navbar가 들어갈 자리</header>
<BrowserRouter>
<div>
<Routes>
<Route path='/' Component={MainEx} />
<Route path='/member/list' Component={MemberList} />
<Route path='/member/insert' Component={MemberInsert} />
<Route path='/member/detail/:username' Component={MemberDetail} />
<Route path='/member/update/:username' Component={MemberUpdate} />
<Route path='/member/delete/:username' Component={MemberDelete} />
</Routes>
</div>
</BrowserRouter>
</div>
);
}
4. MemberEntity, DTO, Repository 생성
- DTO, Repository는 이전과 동일
@Entity
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
@Table(name = "member")
public class MemberEntity {
@Id
@GeneratedValue(generator = "id-uuid") // 아래에서 만든 값을 가져와서 id에 넣어줌.
@GenericGenerator(strategy = "uuid", name = "id-uuid") // 문자열을 중복되지 않게 생성해줌.
private String id;
@Column(unique = true, nullable = false)
private String username;
@Column(nullable = false)
private String name;
private Date createDate;
private Date updateDate;
private String password;
}
5. Insert작업
- 이클립스
<Controller>
@PostMapping("/insert")
public ResponseEntity<?> insert(@RequestBody MemberDTO dto){
Map<String, Object> map = new HashMap<>();
if(dto == null) {
map.put("result", "잘못된 데이터");
return ResponseEntity.badRequest().body(map);
}
if(dto.getUsername() == null) {
map.put("result", "잘못된 데이터");
return ResponseEntity.badRequest().body(map);
}
if(dto.getName() == null) {
map.put("result", "잘못된 데이터");
return ResponseEntity.badRequest().body(map);
}
if(dto.getPassword() == null) {
map.put("result", "잘못된 데이터");
return ResponseEntity.badRequest().body(map);
}
if(dto.getPassword2() == null) {
map.put("result", "잘못된 데이터");
return ResponseEntity.badRequest().body(map);
}
if(!(dto.getPassword().equals(dto.getPassword2()))) {
map.put("result", "잘못된 데이터");
return ResponseEntity.badRequest().body(map);
}
try {
dto = memberService.insert(dto);
map.put("result", dto);
return ResponseEntity.ok().body(map);
} catch (Exception e) {
e.printStackTrace();
map.put("result", "입력 실패");
return ResponseEntity.badRequest().body(map);
}
}
<Service>
public MemberDTO insert(MemberDTO dto) {
MemberEntity entity = new ModelMapper().map(dto, MemberEntity.class);
entity.setCreateDate(new Date());
entity.setUpdateDate(new Date());
entity = memberRepository.save(entity);
dto = new ModelMapper().map(entity, MemberDTO.class);
return dto;
}
- 리액트
function MemberInsert() {
function onSubmitHandler(e){
e.preventDefault();
const formData = new FormData(e.target);
const dto = {
username: formData.get("username"),
name: formData.get("name"),
password: formData.get("password"),
password2: formData.get("password2")
};
insertFetchfn("member", dto);
}
return (
<div>
<h2>회원 가입</h2>
<form action='#' onSubmit={onSubmitHandler}>
아이디: <input name='username' /><br/>
이름: <input name='name' /><br/>
비번: <input name='password' /><br/>
비번 확인: <input name='password2' /><br/>
<button>가입 완료</button>
</form>
</div>
)
}
6. List/Comp작업
- 이클립스
<Controller>
@GetMapping("/all")
public ResponseEntity<?> findAll(){
Map<String, Object> map = new HashMap<>();
try {
List<MemberDTO> list = memberService.findAll();
map.put("result", list);
return ResponseEntity.ok().body(map);
} catch (Exception e) {
e.printStackTrace();
map.put("result", "실패");
return ResponseEntity.badRequest().body(map);
}
}
<Service>
public List<MemberDTO> findAll() {
List<MemberEntity> list_entity = memberRepository.findAll();
List<MemberDTO> list_dto = new ArrayList<>();
for(MemberEntity e : list_entity) {
list_dto.add(new ModelMapper().map(e, MemberDTO.class));
}
return list_dto;
}
- 리액트
- MemberList.js
function MemberList() {
const [members, setMembers] = useState([])
useEffect(()=>{
fetchFn("GET","http://localhost:9005/api/member/all", null)
.then(data=>{
setMembers(data.result);
})
},[])
return (
<div>
<h2>회원 목록</h2>
{
members.length>0 && members.map(member => <MemberComp key={member.id} member={member}/>)
}
</div>
)
}
- MemberComp.js
function MemberComp(props) {
const member = props.member
return (
<div>
<p>
<Link to={`/member/detail/${member.username}`}>
{member.username}
</Link>
</p>
</div>
)
}
7. Details 작업
- 이클립스
<Controller>
@GetMapping("/name/{username}")
public ResponseEntity<?> findByUsername(@PathVariable("username") String username){
Map<String, Object> map = new HashMap<>();
if(username == null) {
map.put("result", "잘못된 정보입니다.");
return ResponseEntity.badRequest().body(map);
}
try {
MemberDTO dto = memberService.findByUsername(username);
map.put("result", dto);
return ResponseEntity.ok().body(map);
} catch (Exception e) {
e.printStackTrace();
map.put("result", "조회 실패");
return ResponseEntity.badRequest().body(map);
}
}
<Service>
public MemberDTO findByUsername(String username) {
MemberEntity entity= memberRepository.findByUsername(username);
if(username == null) {
throw new RuntimeException("아이디 없음");
}
MemberDTO dto = new ModelMapper().map(entity, MemberDTO.class);
return dto;
}
- 리액트
function MemberDetail() {
const username = useParams().username;
const [member, setMember] = useState(null);
useEffect(() => {
fetchFn("GET", `http://localhost:9005/api/member/name/${username}`, null)
.then(data => {
setMember(data.result);
})
}, [username]);
return (
<div>
<h2>회원 자세히 보기</h2>
{member !== null && <>
<div className='member'>
<p>id: {member.id}</p>
<p>username: {member.username}</p>
<p>name: {member.name}</p>
<p>가입일: {member.createDate}</p>
<p>최종 수정일: {member.updateDate}</p>
</div>
<div>
<Link to={"/"} >홈으로</Link> |
<Link to={`/member/update/${username}`} >수정</Link> |
<Link to={`/member/delete/${username}`} >삭제</Link> |
<Link to={"/member/insert"} >등록</Link>
</div>
</>}
</div>
)
}
8. Update 작업
- 이클립스
<Controller>
@PutMapping("")
public ResponseEntity<?> update(@RequestBody MemberDTO dto){
Map<String, Object> map = new HashMap<>();
if(dto == null) {
map.put("result", "잘못된 정보입니다.");
return ResponseEntity.badRequest().body(map);
}
try {
dto= memberService.update(dto);
map.put("result", dto);
return ResponseEntity.ok().body(map);
} catch (Exception e) {
e.printStackTrace();
map.put("result", "수정 실패");
return ResponseEntity.badRequest().body(map);
}
}
<Service>
@Transactional
public MemberDTO update(MemberDTO dto) {
MemberEntity entity = memberRepository.findByUsernameAndPassword(dto.getUsername(), dto.getPassword());
if(entity == null) {
throw new RuntimeException("잘못된 정보입니다.");
}
entity.setName(dto.getName());
entity.setUpdateDate(new Date());
entity = memberRepository.save(entity);
dto = new ModelMapper().map(entity, MemberDTO.class);
return dto;
}
- 리액트
function MemberUpdate() {
const username = useParams().username;
const [member, setMember] = useState(null);
useEffect(()=>{
fetchFn("GET", `http://localhost:9005/api/member/name/${username}`, null)
.then(data=>{
setMember(data.result);
})
},[username]);
function onSubmitHandler(e){
e.preventDefault();
const formData = new FormData(e.target)
const name = formData.get("name");
const password = formData.get("password");
const dto = {
username, name, password
}
fetchFn("PUT", "http://localhost:9005/api/member", dto)
.then(data=>{
window.location.href=`/member/detail/${data.result.username}`;
});
}
function onInputHandler(e){
let val = e.target.value;
let newMember = {...member, [e.target.name]:val};
setMember(newMember);
}
return (
<div>
<h2>회원 정보 수정</h2>
{ member !== null &&
<form action='#' onSubmit={onSubmitHandler}>
username: <input value={username} disabled/><br/>
name: <input name='name' value={member.name} onInput={onInputHandler}/><br/>
password: <input name='password'/><br/>
<button>이름 수정(비번 수정은 다른 곳에서)</button>
</form>
}
</div>
)
}
9. Delete 작업
- 이클립스
<Controller>
@DeleteMapping("")
public ResponseEntity<?> delete(@RequestBody MemberDTO dto){
Map<String, Object> map = new HashMap<>();
if(dto.getUsername()==null) {
map.put("result", "잘못된 데이터");
return ResponseEntity.badRequest().body(map);
}
if(dto.getPassword()==null) {
map.put("result", "잘못된 데이터");
return ResponseEntity.badRequest().body(map);
}
try {
memberService.delete(dto);
map.put("result", "삭제 성공");
return ResponseEntity.ok().body(map);
} catch (Exception e) {
e.printStackTrace();
map.put("result", "삭제 실패");
return ResponseEntity.badRequest().body(map);
}
}
<Service>
@Transactional
public void delete(MemberDTO dto) {
MemberEntity entity = memberRepository.findByUsernameAndPassword(dto.getUsername(), dto.getPassword());
if(entity == null) {
throw new RuntimeException("잘못된 정보입니다.");
}
memberRepository.delete(entity);
}
- 리액트
- return 안에서 작업
- ref / Ref.current.value 사용
function MemberDelete() {
const username = useParams().username;
const passwordRef = useRef();
useEffect(()=>{
passwordRef.current.focus()
})
return (
<div>
<h2>회원 삭제</h2>
username : <input value={username} readOnly /><br/>
password : <input ref={passwordRef} placeholder='비밀번호를 입력하세요' /><br/>
<button onClick={()=>{
const password = passwordRef.current.value;
const dto = {
username, password
};
fetchFn("DELETE", "http://localhost:9005/api/member", dto)
.then(data=>{
console.log(data);
if(data === undefined){ // 실패하면, data에는 아무것도 안 들어감.
passwordRef.current.value=""; //실패하면, password 입력창을 지움
return; //실패하면, 현재 페이지에 남게 함.
}
window.location.href="/";
});
}}>삭제</button>
</div>
)
}