피드관리 - Part 1 피드작성 (1) 해시태그 입력
ㆍProject Diary/React + MariaDB (PicShare WebApp)
사용자는 입력 필드에서 해시태그를 입력할 수 있으며, 이를 추가하거나 삭제할 수 있습니다. 해시태그는 서버로 전송되어 데이터베이스에 저장됩니다.
데이터베이스
- posts 테이블 : 사용자의 게시물 정보를 저장합니다.
- hashtags 테이블 : 해시태그 정보를 저장합니다.
- post_hashtags 테이블 : 게시물과 해시태그 간의 다대다 관계를 관리합니다.
설계 이유
- 효율적인 검색
- 해시태그를 통한 검색 : 해시태그를 인덱싱함으로써 빠른 검색이 가능해집니다. - 다대다 관계 관리(정규화)
- 게시물과 해시태그의 다대다 관계 : 한 게시물에 여러 해시태그가 붙을 수 있고, 한 해시태그가 여러 게시물에 사용될 수 있습니다. 이를 관리하기 위해 post_hashtags 테이블을 사용하여 다대다 관계를 구현합니다. - 데이터 무결성
- 외래 키 제약 조건 : 게시물이나 해시태그가 삭제되면 관련된 post_hashtags 항목도 자동으로 삭제됩니다. - 해시태그 중복 방지
- 해시태그가 이미 존재하는지 확인한 후 존재하지 않으면 새로 추가합니다.
> 외래 키 제약 조건
CONSTRAINT `post_hashtags_ibfk_1` FOREIGN KEY (`postId`) REFERENCES `posts` (`postId`) ON DELETE CASCADE,
CONSTRAINT `post_hashtags_ibfk_2` FOREIGN KEY (`hashtagId`) REFERENCES `hashtags` (`hashtagId`) ON DELETE CASCADE
- ON DELETE CASCADE : 부모 테이블의 행이 삭제되면 관련된 자식 테이블의 행도 자동으로 삭제됩니다.
- ON UPDATE CASCADE : 부모 테이블의 기본 키가 변경되면 관련된 자식 테이블의 외래 키도 자동으로 업데이트됩니다.
프론트엔드
1. 해시태그 입력 기능
1-1 해시태그 변경 핸들러
// 해시태그 변경 핸들러
const handleHashtagChange = (index, value) => {
const newHashtags = [...hashtags]; // 기존 해시태그 배열 복사
newHashtags[index] = value; // 해당 인덱스의 해시태그 변경
setHashtags(newHashtags); // 변경된 해시태그 배열로 상태 업데이트
};
1-2 해시태그 입력 필드
<div className="hashtags">
{hashtags.map((hashtag, idx) => (
<div className="hashtag" key={idx}>
<span>#</span>
<input
type="text"
placeholder={`${idx + 1}번째 해시태그를 입력하세요`}
value={hashtag}
onChange={(e) => handleHashtagChange(idx, e.target.value)}
/>
{idx === hashtags.length - 1 && (
<>
<button type="button" onClick={handleAddHashtag}>
+
</button>
{hashtags.length > 1 && (
<button type="button" onClick={() => handleRemoveHashtag(idx)}>
-
</button>
)}
</>
)}
</div>
))}
</div>
- handleAddHashtag : 새로운 해시태그 입력 필드를 추가합니다.
- handleRemoveHashtag : 기존 해시태그 입력 필드를 삭제합니다.
1-3 해시태그 데이터 전송
서버로 전송하기 전에 해시태그 배열을 문자열로 변환하여 formData에 추가합니다.
formData.append("hashtags", hashtags.join(" "));
- formData.append("hashtags", hashtags.join(" "));
백엔드
1. 해시태그 저장 처리
// 해시태그 저장
const hashtagArray = hashtags
.split(" ")
.map((tag) => (tag.startsWith("#") ? tag : `#${tag}`));
const hashtagInsert = hashtagArray.map((tag) => {
return new Promise((hashtagres, hashtagdata) => {
db.query(
`SELECT hashtagId FROM hashtags WHERE tag = ?`,
[tag],
(err, rows) => {
if (err) {
console.error("hashtag 검색 중 오류:", err);
return hashtagdata(err);
}
if (rows.length > 0) {
// 이미 존재하는 해시태그인 경우
const hashtagId = rows[0].hashtagId;
db.query(
`INSERT INTO post_hashtags (postId, hashtagId) VALUES (?, ?)`,
[postId, hashtagId],
(err, result) => {
if (err) {
console.error("post_hashtags에 삽입 중 오류:", err);
return hashtagdata(err);
}
hashtagres(result);
}
);
} else {
// 새로운 해시태그인 경우
db.query(
`INSERT INTO hashtags (tag) VALUES (?)`,
[tag],
(err, result) => {
if (err) {
console.error("hashtag 삽입 중 오류:", err);
return hashtagdata(err);
}
const hashtagId = result.insertId;
db.query(
`INSERT INTO post_hashtags (postId, hashtagId) VALUES (?, ?)`,
[postId, hashtagId],
(err, result) => {
if (err) {
console.error("post_hashtags에 삽입 중 오류:", err);
return hashtagdata(err);
}
hashtagres(result);
}
);
}
);
}
}
);
});
});
새로운 해시태그는 hashtags 테이블에 삽입되고, 기존 해시태그는 post_hashtags 테이블에 postId와 함께 저장됩니다.
sql 쿼리문
SELECT hashtagId FROM hashtags WHERE tag = ?
- 해시태그 테이블에서 태그가 존재하는지 확인합니다.
INSERT INTO post_hashtags (postId, hashtagId) VALUES (?, ?)
- 이미 존재하는 해시태그의 경우, 해당 해시태그와 게시물의 관계를 post_hashtags 테이블에 저장합니다.
INSERT INTO hashtags (tag) VALUES (?)
- 새로운 해시태그인 경우, hashtags 테이블에 태그를 삽입합니다.
INSERT INTO post_hashtags (postId, hashtagId) VALUES (?, ?)
- 새로운 해시태그를 hashtags 테이블에 삽입한 후, 해당 해시태그와 게시물의 관계를 post_hashtags 테이블에 저장합니다.
예시
가정 #sunny -> 이미 'hashtags' 테이블에 존재
#beach -> 새로운 해시태그결과 1. #sunny의 hashtagId를 가져와 post_hashtags 테이블에 저장
2. # beach 는 hashtags 테이블에 삽입한 후, post_hashtags 테이블에 postId와 함께 저장