최종프로젝트1

링크 : 프로젝트파일 보기(GitHub)

1.주제

관심사, 위치기반 모임 서비스 Meet-Meet

2.웹 기획 순서

앞서 진행했던 프로젝트들에서 큰 틀만 잡고 진행을 하다 생긴 문제들이 많았다. 그래서 이번에는 더 세세한 계획을 세우고 프로젝트를 진행하기로 결정했다. 순서는 다음과 같다.

  1. 프로젝트 기획
    • 웹사이트 목적 : 위치 기반 소셜 네트워크 서비스를 제공해 많은 이용자수를 얻고 이를 통해 광고수익을 얻는다.
    • 웹사이트 컨셉 : 타킷은 공통 관심사를 가진 사람들과 모임을 가지고 싶어하는 사람들이다.
  2. 웹사이트 설계
    • UX 디자인 : 무한 스크롤을 이용해 편하게 스크롤만 내리면 웹페이지 내용이 갱신되면서 쉽고 편한 탐색이 가능케 한다.
    • 웹사이트 구조 설계
      제목 없음
    • 와이어 프레임 (ex. 홈화면)
      와이어프레임
    • 시스템 설계 : 스프링부트, MySql 사용
  3. 디자인 제작
    • 디자인 컨셉 설계 : 담백한 느낌으로 주로 하얀색을 사용 + 인스타그램을 참고
    • 리소스 준비
  4. 웹사이트 개발
    • 프론트엔드 구현
    • 백엔드 구현
    • 테스트

3.엔티티 관계도

여러 테이블이 계정(account) 테이블을 참조하는 형태로 DB를 구성했다.

image

  • 친구기능
    • friend_list 테이블 : 친구가 되는 두 사람이 id1, id2 필드값으로 들어간다. 이때 A와 B가 친구라면 ‘id1 = A, id2 = B’ 인 경우와 ‘id1 = B, id2 = A’ 인 경우로 한 쌍의 친구가 생길 때마다 두 개의 레코드가 생성되게 했다. 이런 구조는 특정 계정의 친구들을 검색할 때 id1이나 id2 중 하나의 필드만 검색하면 해당 계정의 친구들을 검색할 수 있게 하려고 이용했다. 또한 ‘unique(id1, id2)’을 통해 같은 친구정보가 중복되지 않도록 설정했고, account테이블의 account_id를 외래키로 참조할 때 on delete cascade옵션을 사용해 account테이블에서 해당 계정이 삭제되면 friend_list테이블에서도 삭제되게 설정했다.( account_id를 수정하는 기능은 추가하지 않기로 하여 on update cascade는 사용하지 않음 )
      헷갈렸던 부분 1 : on delete cascade 옵션을 주지 않고 해당 레코드를 삭제하면 해당 레코드의 기본키를 참조하고 있던 레코드는 어떻게 될까?
      –> 아무런 설정을 하지 않고 외래키 설정만 추가하여 부모테이블과 자식테이블이 있는 상황에서는 DB자체에서 무결성을 유지하기 위해 부모테이블(참조당하는 테이블)의 레코드를 삭제하거나 참조당하는 고유키를 변경하지 못하게 막는다.(이때 부모테이블의 기본키를 하나의 자식테이블도 참조하지 않고 있으면 삭제하거나 변경이 가능하다.) on delete cascade나 on update cascade는 자식테이블이 부모테이블을 참조하고 있는 상황에서도 부모테이블의 고유키를 변경하거나 삭제하면 해당 고유키를 참조하고 있는 자식테이블의 외래키도 삭제되거나 변경되도록 하는 설정이다.
    • friend_request 테이블 : 친구 요청한 사람, 친구 요청받은 사람이 request_id, requested_id 필드값으로 들어간다. friend_list테이블과 마찬가지로 on delete cascade설정을 통해 account가 삭제되면 해당 account를 참조하고 있는 friend_request테이블 레코드도 삭제되게 설정했다.

나머지 생략…

image

4.친구기능

이번 팀프로젝트에서 친구기능을 주로 담당했기에 친구기능에 대해서만 간단하게 설명한다.

4.1 친구목록

  • Front-End
    1. ‘친구목록’페이지에서 비동기로 현재 접속 중인 계정 파악 후 내 프로필 출력 함수, 친구 목록 출력 함수 호출

image

/* 자바스크립트용 전역변수 선언 */
var User = "?";				// 세션에서 받은 id 저장할 변수
var Username = "?";			// 세션에서 받은 nickname 저장할 변수
var targetFriendId = "?"; 	// 친구목록에서 현재 선택된 친구id를 저장할 변수

/* 서버단 세션에서  id,nickname 받아오기*/
axios
.post("getsession")
.then(
	function(response) {
		User = response.data[0];
		Username = response.data[1];
		
		showMe();
		showFriends();
});
  1. 위에서 받은 내 계정 정보를 파라미터로 넣어 해당 계정 친구목록을 비동기로 요청해 화면 구성
function showFriends(){
	const xhttp = new XMLHttpRequest();
	xhttp.onload = function() {
		let res_data = this.responseText;
		let data = JSON.parse(res_data);

		// 출력 화면 코드 //

	}
	
	xhttp.open("get", `friendlist/findFriendListById1?id1=${User}`, true);
	xhttp.setRequestHeader("Content-type","application/x-www-form-urlencoded;charset=UTF-8");
	xhttp.send();
}
  • Back-End
    1. 세션에 저장된 계정 id, 닉네임 반환
	@ResponseBody
	@PostMapping("/getsession")
	public String[] getSession(HttpSession session) {
		return new String[] { session.getAttribute("accountId").toString(),
				session.getAttribute("nickName").toString() };
	}

  1. 직접 쿼리문을 작성해 친구목록 테이블에서 해당 id와 친구인 계정 id, 닉네임 반환
	@Query("SELECT fl.id, fl.id2.accountId, fl.id2.nickName FROM FriendList fl WHERE fl.id1.accountId=:id1")
	public abstract List<List<String>> findId2ById1AccountId(String id1);

4.2 친구신청

  • Front-End
    1. ‘친구추가’페이지에서 닉네임으로 유저들 검색
var friendsNum = 0;
var perPage = 12;
var pageNum = 0;
var tempData = 0;
var div = 0;
///// 1. 친구를 검색하는 함수 생성 //////
function findFriend() {
	const findUser = document.getElementById("findUser").value;
	const xhttp = new XMLHttpRequest();
    /////  3.받아온 정보로 화면 구성  ////////
	xhttp.onload = function() {   
		let res_data = this.responseText;
		let data = JSON.parse(res_data);

                // 화면 구성 생략 //
									
		}
    /////  2.서버에서 원하는 정보 비동기로 요청  ////////
	xhttp.open("get", `searchUser?searching=${findUser}&id=${User}`, true);
	xhttp.setRequestHeader("Content-type","application/x-www-form-urlencoded;charset=UTF-8");
	xhttp.send();
}
</script>
  1. 검색된 유저들 중 원하는 인원에 해당되는 친구신청 버튼 클릭
/// 1.친구신청하는 함수 생성   ///
function addFriend(v) {
	const findUser = v;
	const xhttp = new XMLHttpRequest();
    /// 3.받아온 정보를 알람으로 출력 ////
	xhttp.onload = function() {
		let res_data = this.responseText;
		alert(res_data);
	}
    /// 2.서버에 정보 비동기로 넘겨주고 결과요청  ///
	xhttp.open("post", `friendlist/post?id1=${User}&id2=${findUser}`, true);
	xhttp.setRequestHeader("Content-type","application/x-www-form-urlencoded;charset=UTF-8");
	xhttp.send();
}
  • Back-End
    1. AccountRepository에서 해당 조건을 만족하는 Account리스트 반환
public interface AccountRepository extends CrudRepository<Account, String>{
	public List<Account> findByNickNameContainingAndAccountIdNot(String searching,String id);
}
  1. 여러 조건을 검증한 뒤 친구요청목록에 저장
// 1.닉네임으로 해당 Account 검색
Optional<Account> result1 = accountRepository.findById(id1);
Optional<Account> result2 = accountRepository.findById(id2);


// 2.검색된 Account로 친구목록에 존재하는지 검사
@Query("SELECT fl FROM FriendList fl WHERE fl.id1.accountId=:id1 AND fl.id2.accountId=:id2")
	public abstract Optional<FriendList> findMyFunction(String id1, String id2);

// 3.모든 검사를 마치면 친구요청목록에 저장
friendRequestRepository.save(modelMapper.map(result, FriendRequest.class));

4.3 친구신청 수락/삭제

  • Front-End
    1. ‘친구요청’페이지에서 친구요청들 확인
// 1.친구요청 목록을 보여주는 함수 생성 //
function showRequest(){
	const xhttp = new XMLHttpRequest();
    // 3. 받아온 정보로 화면구성 //
	xhttp.onload = function() {
		let res_data = this.responseText;
		let data = JSON.parse(res_data);
		
		// 화면 구성 생략 //
	}
    // 2.서버에 정보 비동기로 요청 ///
	xhttp.open("get", `friendrequest/findFriendRequestByRequestedId?requestedId=${User}`, true);
	xhttp.setRequestHeader("Content-type","application/x-www-form-urlencoded;charset=UTF-8");
	xhttp.send();
}
  1. 친구 수락/삭제 버튼을 통해 원하는 작업 진행
function acceptRequest(v1,v2,v3){	
	const request1 = v1;
	const request2 = v2;
	const request3 = v3;
	const xhttp = new XMLHttpRequest();
	xhttp.onload = function() {
		let res_data = this.responseText;
		alert(res_data);
		showRequest();
	}
	xhttp.open("delete", `friendrequest/accept?id=${request1}&id1=${request2}&id2=${request3}`, true);
	xhttp.setRequestHeader("Content-type","application/x-www-form-urlencoded;charset=UTF-8");
	xhttp.send();
}
function deleteRequest(v){	
	const request = v;
	const xhttp = new XMLHttpRequest();
	xhttp.onload = function() {
		let res_data = this.responseText;
		alert(res_data);
		showRequest();
	}
	xhttp.open("delete", `friendrequest/delete?id=${request}`, true);
	xhttp.setRequestHeader("Content-type","application/x-www-form-urlencoded;charset=UTF-8");
	xhttp.send();
}
  • Back-End
    1. 수락
    Optional<Account> result1 = accountRepository.findById(id1);
    Optional<Account> result2 = accountRepository.findById(id2);
    AccountDTO result3 = null;
    AccountDTO result4 = null;
    if (result1.isPresent()) {
        result3 = modelMapper.map(result1.get(), AccountDTO.class);
    }
    if (result2.isPresent()) {
        result4 = modelMapper.map(result2.get(), AccountDTO.class);
    }
    FriendListDTO result = FriendListDTO.builder().id1(result3).id2(result4).build();
    friendListRepository.save(modelMapper.map(result, FriendList.class));
    FriendListDTO resultt = FriendListDTO.builder().id2(result3).id1(result4).build();
    friendListRepository.save(modelMapper.map(resultt, FriendList.class));
  1. 삭제
friendRequestRepository.deleteById(id2);

results matching ""

    No results matching ""