Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FEAT. 인스타그램 크롤링 및 연동 #103

Merged
merged 3 commits into from
Jun 25, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
379 changes: 379 additions & 0 deletions package-lock.json

Large diffs are not rendered by default.

4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,16 @@
"preview": "vite preview"
},
"dependencies": {
"@esbuild/darwin-x64": "^0.21.5",
"@p5-wrapper/react": "^4.4.1",
"@reduxjs/toolkit": "^2.2.5",
"@rollup/rollup-darwin-x64": "^4.18.0",
"apexcharts": "^3.49.1",
"axios": "^1.7.2",
"cheerio": "^1.0.0-rc.12",
"bootstrap": "^5.3.3",
"bootstrap-icons": "^1.11.3",
"chart.js": "^4.4.3",
"cheerio": "^1.0.0-rc.12",
"react": "^18.2.0",
"react-apexcharts": "^1.4.1",
"react-bootstrap": "^2.10.2",
Expand Down
123 changes: 16 additions & 107 deletions src/components/common/keywordGraph/instagramGraph/InstagramGraph.jsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React from "react";
import React, { useEffect, useState } from "react";
import { Line } from "react-chartjs-2";
import {
Chart as ChartJS,
Expand All @@ -12,6 +12,7 @@ import {
CategoryScale,
} from "chart.js";
import styled from "styled-components";
import { getInstagramSocialTrend } from "../../../../lib/apis/social";

ChartJS.register(
LineElement,
Expand All @@ -34,115 +35,23 @@ const ChartWrapper = styled.div`
padding: 10px;
`;

const formatYAxisLabel = (value) => {
if (Math.abs(value) >= 1000000) {
return (Math.abs(value) / 1000000).toFixed(0) + "M";
} else if (Math.abs(value) >= 1000) {
return (Math.abs(value) / 1000).toFixed(0) + "K";
}
return value;
};

const InstagramGraph = () => {
const data = [
{ date: "24.11", posts: 520 },
{ date: "24.12", posts: 5505 },
{ date: "25.01", posts: 5005 },
];

const labels = data.map((item) => item.date);

const lineData = {
labels: labels,
datasets: [
{
label: "포스트 수",
data: data.map((item) => item.posts),
borderColor: "rgba(214, 41, 118, 1)", // Pink color for Instagram aesthetic
borderWidth: 3, // Thicker line
yAxisID: "y1",
},
{
label: "전월 대비 증감",
data: data.map((item, index) => {
if (index === 0) return 0; // First entry has no growth
return item.posts - data[index - 1].posts; // Previous post count minus current post count
}),
backgroundColor: "rgba(214, 41, 118, 0.2)", // Blue color for Instagram aesthetic
borderRadius: 20, // Rounded corners
barThickness: 50, // Thinner bars
yAxisID: "y2",
type: "bar",
},
],
};

const maxDataValue = Math.max(...lineData.datasets[0].data);
const minDataValue = Math.min(...lineData.datasets[0].data);

const range = Math.max(Math.abs(maxDataValue), Math.abs(minDataValue));
const symRange = range + Math.abs(range % 1000);

const options = {
scales: {
y1: {
beginAtZero: true,
position: "left",
grid: {
drawOnChartArea: false,
},
ticks: {
callback: (value) => formatYAxisLabel(value),
},
},
y2: {
beginAtZero: true,
position: "right",
grid: {
drawTicks: true, // Draw horizontal grid lines without displaying labels
display: true,
},
ticks: {
callback: (value) => {
let formattedValue = formatYAxisLabel(Math.abs(value));
if (value > 0) {
return `${formattedValue}`;
} else if (value < 0) {
return `-${formattedValue}`;
}
return formattedValue;
},
},
},
x: {
beginAtZero: true,
},
},
maintainAspectRatio: false,
plugins: {
legend: {
position: "top",
},
tooltip: {
mode: "index",
intersect: false,
},
},
};

// Adjust y-axis range to ensure symmetric
if (Math.abs(maxDataValue) > Math.abs(minDataValue)) {
options.scales.y2.max = symRange;
options.scales.y2.min = -symRange;
} else {
options.scales.y2.max = symRange;
options.scales.y2.min = -symRange;
}

return (
const InstagramGraph = ({ data, lineData, options }) => {
// console.log(lineData, options);
console.log(data, lineData, options);
return data > 0 ? (
<ChartWrapper>
<Line data={lineData} options={options} />
</ChartWrapper>
) : (
<div
style={{
display: "flex",
margin: "0px",
width: "550px",
height: "300px",
padding: "10px",
}}
></div>
);
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,7 @@ import {
generateRandomGradient,
} from "./InstagramHotHashTag.style";

export default function InstagramHotHashTags() {
const [tags, setTags] = useState(["장원영", "스트레이키즈", "차은우"]);

export default function InstagramHotHashTags({ topTags }) {
return (
<div
style={{
Expand All @@ -15,14 +13,16 @@ export default function InstagramHotHashTags() {
gap: "10px",
}}
>
{tags.map((item, index) => {
const gradient = generateRandomGradient();
return (
<StyledHotHashTag key={index} gradient={gradient}>
# {item}
</StyledHotHashTag>
);
})}
{topTags && topTags.length > 0
? topTags.slice(0, 3).map((item, index) => {
const gradient = generateRandomGradient();
return (
<StyledHotHashTag key={index} gradient={gradient}>
# {item}
</StyledHotHashTag>
);
})
: null}
</div>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ const InstagramIndex = ({ index, info, isFirst, isLast }) => {
);
};

export default function InstagramIndexes() {
return (
export default function InstagramIndexes({ tagInfo }) {
return tagInfo ? (
<div
style={{
display: "flex",
Expand All @@ -43,8 +43,18 @@ export default function InstagramIndexes() {
marginBottom: "10px",
}}
>
<InstagramIndex index="75.2K" info="포스트 수" isFirst />
<InstagramIndex index="37%" info="상위 비율" isLast />
<InstagramIndex index={tagInfo[0]} info="포스트 수" isFirst />
<InstagramIndex index={tagInfo[1]} info="상위 비율" isLast />
</div>
) : (
<div
style={{
display: "flex",
flexDirection: "row",
justifyContent: "space-between",
width: "100%",
marginBottom: "10px",
}}
></div>
);
}
14 changes: 9 additions & 5 deletions src/components/common/news/instagram-data/instagram.data.jsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React from "react";
import React, { useEffect } from "react";
import { StyledNewsKeyword } from "./instagram.data.style";
import { StyledNewsDiv } from "./instagram.data.style";
import {
Expand All @@ -11,9 +11,13 @@ import { weekTimeAgo } from "~/utils/utils";
import insta from "~/images/instagram.png";
import { StyledBlurDiv } from "./instagram.data.style";
import InstagramIndexes from "../../keywordGraph/instagramGraph/InstagramIndexes";

export default function InstagramData() {
import { useSelector } from "react-redux";
export default function InstagramData({ tagInfo }) {
const keyword = useSelector((state) => state.keyword.keyword);
//!! 샘플 데이터입니다.
useEffect(() => {
console.log(tagInfo);
}, []);
const insta_sample_data = [
{
caption:
Expand Down Expand Up @@ -59,9 +63,9 @@ export default function InstagramData() {
return (
<StyledNewsDiv>
<StyledNewsKeyword>
<span>"kpop"</span>이 이렇게 언급됐어요
<span>"{keyword}"</span>이 이렇게 언급됐어요
</StyledNewsKeyword>
<InstagramIndexes></InstagramIndexes>
<InstagramIndexes tagInfo={tagInfo}></InstagramIndexes>
<StyledNewsItemParentDiv>
{data.map((e, index) => (
<>
Expand Down
4 changes: 2 additions & 2 deletions src/components/common/news/naver-news/Naver.style.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import styled, {keyframes,css}from "styled-components";
import styled, { keyframes, css } from "styled-components";

export const StyledNewsDiv = styled.div`
export const StyledNewsDiv = styled.div`
width: ${(props) => props.width || "470px"};
height: ${(props) => props.height || "600px"};
background-color: white;
Expand Down
21 changes: 21 additions & 0 deletions src/lib/apis/social.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import axios from "axios";

const BASE_URL = "/api/social";

export const getInstagramSocialTrend = async function getInstagramSocialInfo(
keyword
) {
try {
const { data } = await axios.get(`${BASE_URL}/instagram`, {
params: { word: keyword }, // Use 'params' instead of 'query'
});
console.log("data:", data);
return {
trendData: data.trendData,
topTags: data.topTags,
tagInfo: data.tagInfo,
};
} catch (error) {
console.error("Error fetching Instagram social trend:", error);
}
};
Loading