React

bmsapi 프로젝트 - bmsfront 리액트 (member 패키지)

bumkee25 2023. 4. 24. 17:42

순서: insert - list/comp - detail - update - delete 작업

1.  서로 다른 사이트간의 통신

  1. bmsapi -> http://localhost:9005
  2. 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>
  )
}