diff --git a/src/app/match/[id]/page.tsx b/src/app/match/[id]/page.tsx
index 1b754a4..27375d0 100644
--- a/src/app/match/[id]/page.tsx
+++ b/src/app/match/[id]/page.tsx
@@ -98,8 +98,8 @@ export default function Match({ params }: { params: { id: string } }) {
                         scrollToBottom={scrollToBottom}
                         {...data}
                       />
-                      <CommentList commentList={comments} />
-                      <div ref={scrollRef}></div>
+                      <CommentList.SocketList commentList={comments} />
+                      <li ref={scrollRef}></li>
                     </ul>
                     <CommentForm
                       matchId={params.id}
diff --git a/src/components/match/CommentList/index.tsx b/src/components/match/CommentList/index.tsx
index d03934b..1628aef 100644
--- a/src/components/match/CommentList/index.tsx
+++ b/src/components/match/CommentList/index.tsx
@@ -2,22 +2,35 @@
 
 import { useEffect } from 'react';
 
+import useInfiniteObserver from '@/hooks/useInfiniteObserver';
 import { MatchCommentType } from '@/types/match';
 
 import CommentItem from '../CommentItem';
 
 type CommentListProps = {
   commentList: MatchCommentType[];
-  hasNextPage?: boolean;
-  fetchNextPage?: () => void;
-  isFetching?: boolean;
-  scrollToBottom?: () => void;
+  hasNextPage: boolean;
+  fetchNextPage: () => void;
+  isFetching: boolean;
+  scrollToBottom: () => void;
 };
 
 export default function CommentList({
   commentList,
+  fetchNextPage,
+  hasNextPage,
+  isFetching,
   scrollToBottom,
 }: CommentListProps) {
+  const { ref } = useInfiniteObserver<HTMLDivElement>(
+    async (entry, observer) => {
+      observer.unobserve(entry.target);
+      if (hasNextPage && !isFetching) {
+        fetchNextPage();
+      }
+    },
+  );
+
   useEffect(() => {
     if (!scrollToBottom) return;
 
@@ -26,9 +39,22 @@ export default function CommentList({
 
   return (
     <>
+      <div ref={ref}></div>
       {commentList.map(comment => (
         <CommentItem {...comment} key={comment.commentId} order={1} />
       ))}
     </>
   );
 }
+
+CommentList.SocketList = function SocketList({
+  commentList,
+}: Pick<CommentListProps, 'commentList'>) {
+  return (
+    <>
+      {commentList.map(comment => (
+        <CommentItem {...comment} key={comment.commentId} order={1} />
+      ))}
+    </>
+  );
+};
diff --git a/src/hooks/useInfiniteObserver.ts b/src/hooks/useInfiniteObserver.ts
new file mode 100644
index 0000000..3283634
--- /dev/null
+++ b/src/hooks/useInfiniteObserver.ts
@@ -0,0 +1,33 @@
+import { useCallback, useEffect, useRef } from 'react';
+
+type IntersectHandler = (
+  entry: IntersectionObserverEntry,
+  observer: IntersectionObserver,
+) => void;
+
+export default function useIntersect<T extends HTMLElement>(
+  onIntersect: IntersectHandler,
+  options?: IntersectionObserverInit,
+) {
+  const ref = useRef<T | null>(null);
+  const callback = useCallback(
+    (entries: IntersectionObserverEntry[], observer: IntersectionObserver) => {
+      entries.forEach(entry => {
+        if (entry.isIntersecting) onIntersect(entry, observer);
+      });
+    },
+    [onIntersect],
+  );
+
+  useEffect(() => {
+    if (!ref.current) return;
+
+    const observer = new IntersectionObserver(callback, options);
+
+    observer.observe(ref.current);
+
+    return () => observer.disconnect();
+  }, [ref, options, callback]);
+
+  return { ref };
+}